第四十三天 | 416.分割等和子集 1049.最后一块石头的重量|| 494.目标和 474.一和零

题目:416.分割等和子集

思路:只要找到集合里能够出现sum/2的子集总和,就算是可以分割成两个相同元素和子集了。

1.dp[j]含义:背包容量为j时,放进物品后,背的最大重量为dp[j]

那么如果背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。

2.递推公式:dp[j] = max(dp[j], dp[j - nums[j]] + nums[j])

本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。

3.初始化:如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。

4.遍历顺序:先物品后背包;背包从后到前(若从前到后则每个物品会放入多次)

5.举例推到dp数组:dp[target]里存的是最大重量

代码如下:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        
        //dp[i]中的i表示背包内的总和
        //题目中说:每个数组中的元素不会超过 100,数组大小不会超过 200
        //总和不会大于20000,背包最大只需要其中的一半,所以10001大小就可以了
        vector<int> dp(10001, 0);

        
        for(int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        //库函数一步求和:int sum = accumulate(nums.begin(), nums.end(), 0);
        if(sum % 2 == 1) return false;
        int target = sum / 2;

        //开始01背包
        for(int i = 0; i < nums.size(); i++){
            for(int j = target; j >= nums[i]; j--) {   //每一个元素一定是不可重复放入,所以大小从大到小遍历
                                                       //j代表背包容量,当j>nums[i]时,j - nums[i]才会大于0,背包里才能放下它
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);      //每次都做选择,求每次的最优解
            }
        }
        //集合中的元素正好可以凑成总和target
        if(dp[target] == target) return true;
        return false;
    }
};

题目:1049.最后一块石头的重量||

思路:将石头尽可能的分为重量总和近似相等的两堆,是两堆相撞,得到相撞所剩下的最小值

1.dp[j]含义:背包容量为j时,最大价值为dp[j]。每个石头的重量即为每个石头的价值(这一点要注意)。

        如何定义dp数组大小?极端情况下所有石头一共中3000,因为本题将石头尽可能的分为重量总和近似相等的两堆,所以背包容量1500就够了

2.递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

3.初始化:0

4.遍历顺序

5.打印dp数组

代码如下:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for(int i = 0; i < stones.size(); i++){
            sum += stones[i];
        }
        vector<int> dp(1501, 0);
        int target = sum / 2;      //target只会向下取整
        

        //开始遍历01背包
        for(int i = 0; i < stones.size(); i++){
            for(int j = target; j >= stones[i]; j--){
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }

        return sum - dp[target] - dp[target];
    }
};

题目:494.目标和(可以尝试一下回溯实现,不过会超时)

1.dp[i]含义:装满容量为 j 的背包有 dp[j] 种方法

2.状态转移方程:dp[j] += dp[j - nums[i]];(求装满背包的方法总数都是用这个)

3.初始化:dp[0] = 1,其余为0(这里是求方法数,而不是“最大价值”)

4.遍历顺序

5.打印dp

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(int i = 0; i < nums.size(); i++){
            sum += nums[i];
        }
        if(abs(target) > sum) return 0;    //此时没有方案
        if((target + sum) % 2 == 1) return 0;    //通过解方程可以得到此关系
        int bagSize = (target + sum) / 2;
        vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;      //初始化要注意
        for(int i = 0;  i < nums.size(); i++){
            for(int j = bagSize; j >= nums[i]; j--){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
};

其实我们需要求的方法总数一直存放在dp[bagSize]里,对于每一次的外层for循环而言,只用dp[bagSize] += dp[bagSize - nums[i]]的这次运算是我们最后将要输出的;而其他格子里的dp[j] += dp[j - nums[i]]的这些运算,也是为了在下一层里做准备。

题目:474.一和零

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

  • 输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3

  • 输出:4

  • 解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。 其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:

  • 输入:strs = ["10", "0", "1"], m = 1, n = 1
  • 输出:2
  • 解释:最大的子集是 {"0", "1"} ,所以答案是 2 。

提示:

  • 1 <= strs.length <= 600
  • 1 <= strs[i].length <= 100
  • strs[i] 仅由 '0' 和 '1' 组成
  • 1 <= m, n <= 100

本题背包变为了两个维度:重量a和重量b。需要定义二维dp数组。

1.dp[i][j]数组含义:尽量装满容量为i、j的背包,有 dp[i][j]这么多个物品

        最多有i个0和j个1的strs的最大子集的大小为dp[i][j]

2.递推公式:dp[i][j] = max(dp[i][j], dp[i - x][j - y] + 1);

3.初始化:都初始化为0

4.遍历顺序:先字符从前到后,在背包,背包两个维度都是从后向前。

5.打印dp

代码如下:

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(101, vector<int>(101, 0));
        for(string str : strs){
            int x = 0;
            int y = 0;
            for(char c : str){
                if(c == '0') x++;
                if(c == '1') y++;
            }
            for(int i = m; i >= x; i--){
                for(int j = n; j >= y; j--){
                    dp[i][j] = max(dp[i][j], dp[i - x][j - y] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

总结一下:

纯背包:给定背包容量,装满这个背包的最大价值

分割等和子集:给定背包容量,能不能装满这个背包?

最后一块石头的重量:给出背包最大重量,尽可能装,问最大能装多少

目标和:给定背包容量,问有多少种方式能装满

一零和:给定背包容量(二维),尽量装满背包有多少个物品

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-25 21:20:18       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-25 21:20:18       106 阅读
  3. 在Django里面运行非项目文件

    2024-05-25 21:20:18       87 阅读
  4. Python语言-面向对象

    2024-05-25 21:20:18       96 阅读

热门阅读

  1. flask中的路由是什么意思

    2024-05-25 21:20:18       28 阅读
  2. 查询MongoDB中某个数据库的占用空间大小

    2024-05-25 21:20:18       33 阅读
  3. 信息安全法律法规复习

    2024-05-25 21:20:18       34 阅读
  4. 详解 Scala 的函数式编程

    2024-05-25 21:20:18       26 阅读
  5. Pytorch-06 使用GPU加速计算

    2024-05-25 21:20:18       31 阅读
  6. Pytorch-07 完整训练测试过程

    2024-05-25 21:20:18       33 阅读
  7. c++翻转一个无符号数的二进制位

    2024-05-25 21:20:18       32 阅读
  8. C++11std::bind的简单使用

    2024-05-25 21:20:18       31 阅读
  9. el-select 组件获取整个对象

    2024-05-25 21:20:18       36 阅读