【DP】从【零钱兑换】问题看01背包和完全背包问题

https://leetcode.cn/problems/coin-change/description/?envType=study-plan-v2&envId=top-interview-150

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0

        for i in range(1, amount + 1):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i], dp[i - coin] + 1)

        return dp[amount] if dp[amount] != float('inf') else -1

完全背包问题裸题

转载自OI wiki
01背包问题状态转移方程

文章还提到了错误的算法,为什么会错误呢,看一下情况:
如果使用我之前提到的错误算法(从小到大更新背包容量)处理同一个例子,我们会看到不同的结果。错误的方法如下:

for i in range(1, n + 1):  # 遍历物品
    for l in range(0, W - w[i] + 1):  # 从小到大遍历背包容量
        f[l + w[i]] = max(f[l] + v[i], f[l + w[i]])

还是使用之前的物品和背包情况:

  • 物品1:重量3,价值30
  • 物品2:重量4,价值50
  • 背包的总容量是7

来看看使用错误方法时会发生什么:

  1. 初始化f[0...7]为0。
  2. 处理物品1:
    • 从容量0开始,只有容量3及以上的f可以被更新。
    • 更新后,f[3]=30, f[4]=30, f[5]=30, f[6]=30, f[7]=30
  3. 处理物品2:
    • 从容量0开始,对于每个lf[l + 4]可以被更新。
    • 因为从小到大的更新方式,f[4]先被更新为50,然后在处理到l=4时,f[8]会被尝试更新(尽管f[8]超出了背包的容量),而f[4]作为之前更新过的值,能在此基础上再加上物品2的价值,导致物品2被错误地考虑了两次。

最终,f数组在容量4的位置上可能反映的是物品2被放入两次的情况(实际上因为数组索引不会到8,但逻辑上这种重复使用已经发生),如果背包容量足够大,你会看到类似的错误累加。

这个错误方法虽然在本例中因为背包容量限制没有显现出非法值,但在更复杂或容量更大的情况下,会导致算法错误地计算出某些物品可以被多次加入背包,从而得到错误的最大价值。这种情况下,算法的输出不符合01背包的定义,即每个物品至多被放入一次。

posted @ 2024-04-26 02:12  peterzh6  阅读(27)  评论(0)    收藏  举报