【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
文章还提到了错误的算法,为什么会错误呢,看一下情况:
如果使用我之前提到的错误算法(从小到大更新背包容量)处理同一个例子,我们会看到不同的结果。错误的方法如下:
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
来看看使用错误方法时会发生什么:
- 初始化
f[0...7]
为0。 - 处理物品1:
- 从容量0开始,只有容量3及以上的
f
可以被更新。 - 更新后,
f[3]=30
,f[4]=30
,f[5]=30
,f[6]=30
,f[7]=30
。
- 从容量0开始,只有容量3及以上的
- 处理物品2:
- 从容量0开始,对于每个
l
,f[l + 4]
可以被更新。 - 因为从小到大的更新方式,
f[4]
先被更新为50,然后在处理到l=4
时,f[8]
会被尝试更新(尽管f[8]
超出了背包的容量),而f[4]
作为之前更新过的值,能在此基础上再加上物品2的价值,导致物品2被错误地考虑了两次。
- 从容量0开始,对于每个
最终,f
数组在容量4的位置上可能反映的是物品2被放入两次的情况(实际上因为数组索引不会到8,但逻辑上这种重复使用已经发生),如果背包容量足够大,你会看到类似的错误累加。
这个错误方法虽然在本例中因为背包容量限制没有显现出非法值,但在更复杂或容量更大的情况下,会导致算法错误地计算出某些物品可以被多次加入背包,从而得到错误的最大价值。这种情况下,算法的输出不符合01背包的定义,即每个物品至多被放入一次。