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

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

和分割等和子集的题目思路类似,要想最后剩余的石头重量最小,需要将石头尽可能的分成两份重量接近的石头,互相碰撞,才能保证剩下的重量最小。

此时target就为sum/2,本题物品的重量和价值都为石头的重量

  1. dp数组及下标含义
    dp[j]:表示到容量为j背包中的最大重量

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

  3. dp数组初始化
    容量为0的背包最大可以装的重量也为0,其余下标也为0防止被覆盖

  4. 遍历顺序
    使用一维数组,在外层遍历物品,内层遍历背包,内层采用倒序遍历防止多重背包问题,物品被重复利用

  5. 举例推导dp数组
    举例,输入:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:
    代码随想录算法训练营第四十三天| 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第1张图片

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = accumulate(stones.begin(), stones.end(), 0);
        cout<<sum<<endl;
        int target = sum / 2;
        vector<int> dp(target + 1, 0);
        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. 目标和

此题比较难想,使用的方法非常巧妙

本题要如何使表达式结果为target,

既然为target,那么就一定有 left组合 - right组合 = target。

left + right = sum,而sum是固定的。right = sum - left

公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。

target是固定的,sum是固定的,left就可以求出来。

此时问题就是在集合nums中找出和为left的组合。

这样题目又跟上面一样了。

  1. 确定dp数组及下标含义
    dp[j]:填满j(包括j)这么大容积的包,有dp[j]种方法(注意此处是求的方法)

  2. 确定递推公式
    只要搞到nums[i]),凑成dp[j]就有dp[j - nums[i]] 种方法。
    例如:dp[j],j 为5,
    已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
    已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
    已经有一个3(nums[i]) 的话,有 dp[2]种方法 凑成 容量为5的背包
    已经有一个4(nums[i]) 的话,有 dp[1]种方法 凑成 容量为5的背包
    已经有一个5 (nums[i])的话,有 dp[0]种方法 凑成 容量为5的背包
    那么凑整dp[5]有多少方法呢,也就是把所有的 dp[j - nums[i]] 累加起来。

    所以求组合类问题的公式,都是类似这种:

    dp[j] += dp[j - nums[i]]
    

    这个公式在后面在讲解背包解决排列组合问题的时候还会用到!

  3. 初始化
    从递归公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递归结果将都是0。
    如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。

    所以本题我们应该初始化 dp[0] 为 1。

  4. 确定遍历顺序
    一维数组的遍历,nums放在外循环,target在内循环,且内循环倒序。

  5. 举例
    输入:nums: [1, 1, 1, 1, 1], S: 3

    bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4

    dp数组状态变化如下:
    代码随想录算法训练营第四十三天| 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第2张图片

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int n : nums) sum += n;
        if ((sum + target) % 2 == 1 || abs(target) > sum) return 0; //如果不能平均分成两份的话,没有组合,当target绝对值比sum大的时候也不行
        int left = (sum + target) / 2;
        vector<int> dp(left + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = left; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[left];
    }
};

474.一和零

此题仍然是0-1背包问题,不是多重背包问题

本题中strs 数组里的元素就是物品,每个物品都是一个!

而m 和 n相当于是一个背包,两个维度的背包。

这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品。

  1. 确定dp数组以及下标的含义
    dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。

  2. 确定递推公式
    dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

    dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。(因为这里是子集的大小也就是子集元素的个数,所以再加上本个字符串就是加1)

    然后我们在遍历的过程中,取dp[i][j]的最大值。

    所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);

  3. 初始化
    因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

  4. 遍历顺序
    外层是遍历物品,这里也就是遍历字符串数组,内层是遍历背包,此题要遍历两层背包,因为是二维的,先后顺序可以交换

  5. 举例
    以输入:[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例

    最后dp数组的状态如下所示:
    代码随想录算法训练营第四十三天| 1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第3张图片

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int>(n +1));
        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];
    }
};

你可能感兴趣的:(leetcode刷题打卡,算法,动态规划,leetcode)