问题描述:一个小偷在商店发现了 n 个商品,第 i 个商品的价值是 v i v_i vi,重量是 w i w_i wi,价值和重量都是整数,小偷的背包最多容纳 W 重量的物体,求一个最大收益的方案。
Dynamic Programming
这是一个离散问题,每个商品要么全拿进背包,要么一点儿不拿。
考虑第 i 个商品,如果
w
i
>
W
w_i > W
wi>W,是拿不进去的;
如果
w
i
≤
W
w_i\leq W
wi≤W, 小偷可以选择拿或者不拿,如果拿,背包容量变成了
W
−
w
i
W-w_i
W−wi,小偷要用剩余的容量去拿剩下的商品;如果不拿,容量仍然是 W, 以 W 来容纳剩下的商品。用
d
p
(
i
,
K
)
dp(i,K)
dp(i,K) 表示背包容量为 K 时,前 i 个商品能放进背包的最大价值,则方程可以写为:
d
p
(
i
,
K
)
=
{
d
p
(
i
−
1
,
K
)
if
w
i
>
K
m
a
x
{
d
p
(
i
−
1
,
K
−
w
i
)
+
v
i
,
d
p
(
i
−
1
,
K
)
}
if
w
i
≤
K
dp(i,K) = \begin{cases}dp(i-1,K)&\text{if }w_i>K \\ max\{\ dp(i-1,K-w_i)+v_i,\ dp(i-1,K)\ \}&\text{if }w_i\le K \end{cases}
dp(i,K)={dp(i−1,K)max{ dp(i−1,K−wi)+vi, dp(i−1,K) }if wi>Kif wi≤K
自底向上的代码 (python 3):
def Knapsack(value,weight,W):
n = len(value)
dp = [ [0 for i in range(W+1)] for j in range(n)] #记录最大价值
record = [ [0 for i in range(W+1)] for j in range(n)] #记录放进背包里的元素编号
for K in range(1,W+1):
for i in range(n):
if weight[i] > K:
dp[i][K] = dp[i-1][K] if i > 0 else 0
else:
dp[i][K] = max( dp[i-1][K] ,dp[i-1][K-weight[i]] + value[i]) if i>0 else value[i]
if not( i > 0 and dp[i-1][K] > dp[i-1][K-weight[i]] ):
record[i][K] = 1
lst = []
def recordNum(i,K):
if i < 0 : return
if record[i][K] == 1:
lst.append[i]
recordNum(i-1,K-weight[i])
else:
recordNum(i-1,K)
return dp[n][W],lst
复杂度:时间复杂度 O ( n W ) O(nW) O(nW),空间复杂度 O ( n W ) O(nW) O(nW)。