背包问题总结
-
分类
物品不重复 -> 01背包
物品可重复 -> 完全背包
-
时间复杂度:O(m*n),m为物品个数,n为背包最大容量。
空间复杂度:一维dp -> O(n),二维dp -> O(n*m)
-
应用
- 能否装满背包 -> dp[j] = max(dp[j], dp[j - weight[i]] + values[i]),返回判断dp[target] == target?
- 装满背包有几种方法 -> dp[j] += dp[j - weight[i]]
- 背包装满最大价值 -> dp[j] = max(dp[j], dp[j - weight[i]] + values[i]),返回dp[target]
- 装满背包所有物品的最小个数 -> dp[j] = min(dp[j - weight[i]] + 1,dp[j])
-
遍历顺序
-
01背包二维dp:背包容量从小到大,for嵌套顺序随意。
-
01背包一维dp:背包容量从大到小,必须先遍历物品,再嵌套遍历背包容量。
以dp[j] = max(dp[j], dp[j - weight[i]] + values[i])为例:
背包容量从大到小 -> 因为一维数组是二维的压缩,而dp[j]依赖于dp[i](i < j)的计算,即右边依赖左边←,所以遍历方向应与依赖方向相同←,此时dp[j]依赖的数据是上一轮计算结果,正确√。否则,dp[j]依赖本轮计算结果,这将导致重复选取物品的问题。
先遍历物品,再嵌套遍历背包容量 -> 因为背包容量必须从大到小,则若先遍历背包容量,再嵌套遍历物品,会导致dp[j] = max(values[i])的问题,因为dp[j - weight[i]]都是空的。
其实展开成二维数组就懂了,右下数据依赖左上,所谓滚动数组。
-
完全背包一维dp:背包容量从小到大,for嵌套顺序随意。
完全背包可以随意选重复物品,所以背包从小到大遍历;因为背包从小到大遍历,所以不管嵌套顺序如何,右下数据都依赖左上。
但是特定场景下for顺序有讲究:
dp[j] += dp[j - weight[i]]
⭐ 组合:必须先遍历物品,再嵌套遍历背包容量。
⭐ 排序:必须先遍历背包容量,再嵌套遍历物品。
其实展开成二维数组就懂了,先遍历物品,再嵌套遍历背包容量,一行一行的下去,意思是一个一个把可选物品摆桌上,每个不同容量的背包同步先选一点点,到最后可选物品全摆出来,不同容量背包也进化完毕了。
-