背包问题
- 01背包问题
- 这是最基本的背包问题,每个物品最多只能放一次。
- 完全背包问题
- 第二个基本的背包问题模型,每种物品可以放无限多次。
- 多重背包问题
- 每种物品有一个固定的次数上限。
01背包问题
题目
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
基本思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]} // v >= c[i]
若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v]
;如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]
的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]
再加上通过放入第i件物品获得的价值w[i]
。
优化空间复杂度
以上方法的时间和空间复杂度均为O(N*V)
,其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)
。
由上述状态转移方程可知,dp[i][j]
的值只与dp[i-1][0,...,j-1]
有关,所以我们可以采用动态规划常用的方法(滚动数组)对空间进行优化(即去掉dp的第一维)。需要注意的是,为了防止上一层循环的dp[0,...,j-1]
被覆盖,循环的时候 j 只能逆向枚举(空间优化前没有这个限制)。
代码
class Solution(object):
def backpackI(self, w, c, V):
"""
w: 物品的价值列表
c: 物品的体积列表
V: 背包的体积
"""
N = len(c)
f = [0]*N
for i in range(N):
for v in range(V, c[i]-1, -1):
// 必须逆向枚举
f[v]=max{
f[v],f[v-c[i