代码随想录第四十四天|完全背包、Leetcode518. 零钱兑换 II、Leetcode377. 组合总和 Ⅳ

代码随想录第四十四天|完全背包、Leetcode518. 零钱兑换 II、Leetcode377. 组合总和 Ⅳ

  • 完全背包
  • Leetcode518. 零钱兑换 II
    • 要记得组合问题的递推公式是`dp[j]+=dp[j-nums[i]];`,且dp[0]必须初始化为1
  • Leetcode377. 组合总和 Ⅳ

完全背包

文章链接:完全背包
01背包问题唯一的不同点,就在于完全背包的货物可以重复使用,表现在应用中就是同一数据可以复用。而表现在代码中,就是遍历顺序的改变:

01背包外层for循环必须为物品内层for循环必须为倒序遍历背包容量,原因是:

  1. 递推公式为dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);,如果外层为背包容量,那么dp[j]一开始都是0,递推公式就变成了dp[j] = max(dp[j], 0+ value[i]);,最后只会放进一个价值最高的物品,而不是组合起来的一组物品;
  2. 而倒序是因为dp[j]的更新过程会用到上一轮dp[j - weight[i]]的数据,如果把前置位的数据先更新(覆盖),那么上一轮遍历的数据就被覆盖掉了,还用啥去。

完全背包就不讲究内外层循环了,只有正序遍历背包容量这一条,原因是:

  1. 因为其正序遍历的特性,你遍历到dp[j]的时候,dp[j-xxx]一定是遍历过了,有值的,所以就无所谓了
  2. 而正序举个例子就懂了,物品0的重量weight[0] = 1,价值value[0] = 15,
    dp[1] = dp[1 - weight[0]] + value[0] = 15
    dp[2] = dp[2 - weight[0]] + value[0] = dp[1] + value[0] = 30
    可以发现遍历容量2的时候套用了容量1的最优解,把0用了两遍,所以只有数据可以复用时能正序

这玩意挺绕,昨天刚学会今天又忘了 ,所以记住01背包外层物品内层倒序容量完全背包正序遍历背包容量,就得了。。。

Leetcode518. 零钱兑换 II

题目链接:Leetcode518. 零钱兑换 II

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);//dp[j]代表总金额为j,的最大方案数
        dp[0]=1;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j]+=dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

要记得组合问题的递推公式是dp[j]+=dp[j-nums[i]];,且dp[0]必须初始化为1

Leetcode377. 组合总和 Ⅳ

题目链接:Leetcode377. 组合总和 Ⅳ
这个还不错,自己想出来怎么改了
唯独对一个测试用例的判断有点问题,因为力扣给了个两数相加超过INT_MAX的用例,所以加上判断dp[j]

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1,0);
        dp[0]=1;
        for(int j=1;j<=target;j++){
            for(int i=0;i<nums.size();i++){         
                if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]]) dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[target];
    }
};

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