代码随想录算法训练营第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,我以前以为是因为只有一层数组,这样内外反了存不住东西,其实不是的。还是为了让一个物品只被考虑一次。

 

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

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

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

// 先遍历物品,在遍历背包
void test_CompletePack() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;
    vector<int> 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<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;

    vector<int> 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<int>& coins) {
        vector<int> dp(amount+1,0);
        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];
    }

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

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

因为物品在外层的话,其中两个物品,比如 1和3,一定只会出现先1后3,或者先3后1.但是如果物品在内层,背包容量在外层,然后物品是 123,整个循环就会 1 2 3 1 2 3 1 2 3.。。1 3和3 1 的组合都可能出现。所以就变成排列而不是组合了。 (自己的理解,不确定,应该是对的)

# 377. 组合总和 Ⅳ

int combinationSum4(vector<int>& nums, int target) {
        vector<long long> 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]]<INT_MAX) 
                    dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }

其实就是零钱兑换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]]
●求装满背包的方法:求组合是物品在外层,求排列是物品在内层

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值