代码随想录算法训练营第42天|1049. 最后一块石头的重量 II 、494. 目标和 、474.一和零

文章目录

  • 1049. 最后一块石头的重量 II
    • 思路
    • 代码
  • 494. 目标和
    • 思路
    • 代码
  • 474.一和零
    • 思路
    • 代码

1049. 最后一块石头的重量 II

题目链接:1049. 最后一块石头的重量 II
文章讲解:代码随想录|1049. 最后一块石头的重量 II

思路

分成两堆,使得差最小→尽可能接近sum/2→01背包问题
1.dp[j]:容量为j的背包,最多可以装的重量
2.dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
3.因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖
4.物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历
5.举例推导dp数组
代码随想录算法训练营第42天|1049. 最后一块石头的重量 II 、494. 目标和 、474.一和零_第1张图片

代码

lass Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        vector<int> dp(15001, 0);
        int sum = 0;
        for (int i = 0; i < stones.size(); i++) sum += stones[i];
        int target = sum / 2;
        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. 目标和

题目链接:494. 目标和
leetcode官方题解:494. 目标和
文章讲解:代码随想录|494. 目标和

思路

将nums分为正数集合和负数集合,所以:正 + 负 = target, 正 - 负 = sum
正 = (target + sum) / 2,如果(target + sum) / 2为小数的话,说明不存在,直接返回0
问题转换为:将nums中的物体放入背包大小为(target + sum) / 2,正好放满有多少种方式,使用二维01背包问题解决(二维相比于一维更好理解)
1.dp[i][j]:在nums的0到i中选择元素,使这些元素之和等于j的方案数,则最终答案为dp[nums.size()][ (target + sum) / 2]
2.如果j < nums[i], 则不能选nums[i],此时有dp[i][j] = dp[i - 1][j];如果j >= nums[i],则有不选nums[i]和选nums[i]两种情况使得元素之和等于j,dp[i][j]=dp[i−1][j]+dp[i−1][j−num[i]]
3.dp[0][0]=1,其他为0
4.正序

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if(abs(target) > sum) return 0;
        if((sum + target) % 2 == 1) return 0;
        int pos = (target + sum) / 2;
        vector<vector<int>> dp(nums.size() + 1, vector<int>(pos + 1, 0));
        dp[0][0] = 1;
        for(int i = 1; i <= nums.size(); i++){
            for(int j = 0; j <= pos; j++){
                dp[i][j] = dp[i - 1][j];
                if(j >= nums[i - 1])dp[i][j] += dp[i - 1][j - nums[i - 1]];
            }
        }
        return dp[nums.size()][pos];
    }
};

474.一和零

题目链接:474.一和零
文章讲解:代码随想录|474.一和零
视频讲解:474.一和零

思路

本题的物品是字符串数组中的字符串,背包容量有两个维度,一个是0的最大个数,一个是1的的最大个数。
还是01背包问题,只是背包容量有两个维度
1.dp[i][j]:最多有i个0和j个1的strs的最大子集的大小
2.dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
3.dp[0][0] = 0
4.外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历

代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
        for (string str : strs) { // 遍历物品
            int oneNum = 0, zeroNum = 0;
            for (char c : str) {
                if (c == '0') zeroNum++;
                else oneNum++;
            }
            for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

你可能感兴趣的:(算法)