代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ

# 完全背包

他的遍历顺序让我又回顾了01背包的遍历顺序,发现自己还是没搞清。于是这回又仔细想想。

对于1d的01背包,保证每个物品只考虑一次的两个要点:

1.背包容量从大到小 2. 物品外层,背包容量内层 

对于1(物品外层时),如果是从小到大:要计算dp[j] =max(dp[j-w[i]]...) 使用的dp[j-w[i]]已经是更新过的了,就是比如要计算容量为7的时候,要通过考虑容量为3的时候的最大value+考虑要不要本层i这个重量为4的物品,但是由于这个7和3是在同一个i,就是“容量为3的时候的最大value” 这里已经考虑过了要不要“本层i这个重量为4的物品”,所以就重复考虑了。 而从大到小的话,左边的那些格子都是没更新的,从上一层传下来的,都是只考虑到第i-1个物品的,就完全符合一个物品只能用一次。

对于2,我以前以为是因为只有一层数组,这样内外反了存不住东西,其实不是的。还是为了让一个物品只被考虑一次。

代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第1张图片代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第2张图片 

 总的来说,我觉得得出正确的遍历顺序的逻辑是:01背包,不管背包容量是从大到小还是从小到大,都应该是物品在外面 (见上图背包不管什么顺序,背包在外层都不对)。先得出这一点,然后再用物品在外才对的前提,继续推导,实验,得出背包容量必须从大到小(上面第一点)

总之,1d的01背包,为了物品只考虑一次,物品外层背包容量内层=>而且容量从大到小

但是1d 完全背包,每个物品可以考虑多次,无数次,所以内层外层可以互相换,都可以。但是背包容量还是要从小到大这是gpt说的原因,虽然我没有很懂:

代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第3张图片

// 先遍历物品,在遍历背包
void test_CompletePack() {
    vector weight = {1, 3, 4};
    vector value = {15, 20, 30};
    int bagWeight = 4;
    vector dp(bagWeight + 1, 0);
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagWeight] << endl;
}
int main() {
    test_CompletePack();
}
// 先遍历背包,再遍历物品
void test_CompletePack() {
    vector weight = {1, 3, 4};
    vector value = {15, 20, 30};
    int bagWeight = 4;

    vector dp(bagWeight + 1, 0);

    for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
        for(int i = 0; i < weight.size(); i++) { // 遍历物品
            if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagWeight] << endl;
}
int main() {
    test_CompletePack();
}

# 518. 零钱兑换 II

终于自己写出来一个背包呜呜 秒出,因为之前01背包 494目标和 和本题很像 

求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]];”

int change(int amount, vector& coins) {
        vector dp(amount+1,0);
        dp[0]=1;

        for(int i=0;i

虽然很快做出来了,但还是有很多没考虑到的要点:初始化很重要

代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第4张图片

然后遍历顺序我只是用了default,没考虑到背包容量在外层不行。本题大难点!

代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第5张图片

代码随想录算法训练营第44天 | 动态规划 part06 ● 完全背包 ● 518. 零钱兑换 II ● 377. 组合总和 Ⅳ_第6张图片因为物品在外层的话,其中两个物品,比如 1和3,一定只会出现先1后3,或者先3后1.但是如果物品在内层,背包容量在外层,然后物品是 123,整个循环就会 1 2 3 1 2 3 1 2 3.。。1 3和3 1 的组合都可能出现。所以就变成排列而不是组合了。 (自己的理解,不确定,应该是对的)

# 377. 组合总和 Ⅳ

int combinationSum4(vector& nums, int target) {
        vector dp(target+1,0);
        dp[0]=1;

        for (int j = 0; j <= target; j++) { // 遍历背包容量
            for (int i = 0; i < nums.size(); i++) { // 遍历物品
                if (j - nums[i] >= 0 && dp[j]+dp[j - nums[i]]

其实就是零钱兑换2 由组合变成排列。

有个data type问题,因为题干说:The test cases are generated so that the answer can fit in a 32-bit integer.,但是test case又有加起来或超过 int max的组合。就是答案保证了用int能装下,但是我们尝试过程中 和 可能很大,所以要把太大的直接不要了。


总结:

●完全背包1d: 物品在内外层都可以,背包容量一定从小到大
●求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]]
●求装满背包的方法:求组合是物品在外层,求排列是物品在内层

你可能感兴趣的:(代码随想录一刷,算法,动态规划,leetcode,c++)