动态规划——dp的含义归类(完全背包和01背包区别)

  1. 用dp表示总价值
  2. 用dp表示组合数
    给出一个总数,一些物品,问能否凑成这个总数。

零钱兑换II https://leetcode.cn/problems/coin-change-ii/

完全背包

二维写法

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

一维写法

// 先遍历物品,再遍历背包
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]);

    }
}

为什么要先遍历物品,在遍历背包呢? (灵魂拷问)

其实纯完全背包,先遍历物品,再遍历背包 与 先遍历背包,再遍历物品都是可以的。

零钱兑换II 不是纯完全背包(求是否能装满背包),而是求装满背包有几种方法。

这里在遍历顺序上可就有说法了。

  • 如果求组合数就是外层for循环遍历物品,内层for遍历背包。
  • 如果求排列数就是外层for遍历背包,内层for循环遍历物品。

为什么会有这种区别?

组合数 是不考虑顺序的,即只关心哪些硬币被选中了,不关心它们的顺序。第一个代码块中,每个硬币依次处理,确保了每个硬币只会按照固定顺序被考虑,因此不会重复计算顺序不同但硬币相同的组合。

排列数 是考虑顺序的,即不同顺序的硬币视为不同的方案。第二个代码块中,每次更新金额时,都考虑了所有硬币的排列,因此会计算所有可能的排列

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

每次更新容量时,遍历一遍所有物品作为当前可能要选择的物品

一维

遍历的时候和01背包不同,01背包需要内层(容量)倒序遍历。而完全背包不需要。这是因为完全背包的递推公式不一样:

如求组合数的递推公式:

完全背包:
二维递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]
压缩成一维:dp[j] += dp[j - coins[i]]

01背包:
二维递推公式:dp[i][j] = dp[i - 1][j] + dp[i-1][j - coins[i]]
压缩成一维:dp[j] += dp[j - coins[i]]

区别: 01背包需要内层倒序遍历,防止覆盖前一层的值;完全背包不需要倒序遍历。

总结

求组合数:518.零钱兑换II
求排列数:377. 组合总和 Ⅳ 70. 爬楼梯进阶版(完全背包)
求最小数:322. 零钱兑换 、279.完全平方数

posted @   NeroMegumi  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示