问题
给定n个物品和一背包,物品i的重量是wi,其价值是vi,背包的容量是m,问如何选择装入背包中的物品总价值最大?
解答思路
a) 把背包问题抽象化(X1,X2,…,Xn,其中 Xi 取0或1,表示第 i 个物品选或不选,类似指示器随机变量),Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积(重量);
b) 建立模型,即求
max(V1X1+V2X2+…+VnXn)
;
c) 约束条件,
W1X1+W2X2+…+WnXn<capacity
;
d) 定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值;
e) 最优子结构是动态规划的基础,是指“一个问题的最优解包含其子问题的最优解”。判断该问题是否满足最优子结构,采用反证法证明:
假设(X1,X2,…,Xn)是01背包问题的最优解,则有(X2,X3,…,Xn)是其子问题的最优解;
假设(Y2,Y3,…,Yn)是上述问题的子问题最优解,则理应有(V2Y2+V3Y3+…+VnYn)+V1X1 > (V2X2+V3X3+…+VnXn)+V1X1;
而(V2X2+V3X3+…+VnXn)+V1X1=(V1X1+V2X2+…+VnXn),则有(V2Y2+V3Y3+…+VnYn)+V1X1 > (V1X1+V2X2+…+VnXn);
该式子说明(X1,Y2,Y3,…,Yn)才是该01背包问题的最优解,这与最开始的假设(X1,X2,…,Xn)是01背包问题的最优解相矛盾,故01背包问题具有最优子结构。
物品的加入顺序不对解答造成影响。
f) 寻找递推关系式,面对当前商品有两种可能性:
第一,当前背包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即V(i,j)=V(i-1,j);
第二,还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即V(i,j)=max{ V(i-1,j),V(i-1,j-wi)+vi}
其中V(i-1,j)表示不装,V(i-1,j-w(i))+v(i) 表示装了第i个商品,背包容量减少w(i)但价值增加了v(i),同时剩余的物品应该是前i-1个物品;
由此可以得出递推关系式:
1)
j<w(i)V(i,j)=V(i−1,j)
2)
j>=w(i)V(i,j)=max{V(i−1,j),V(i−1,j−w(i))+v(i)}
显然其具有重叠子问题。
易得反例,其不能使用贪心算法,但是分数背包可以。
时间复杂度是进行填表
O(mn)
,空间复杂度是
O(mn)
,也就是表。
举例
输入:
背包承重:5
物品序号\项目 | 重量 | 价值 | 单位重量价值(价值/重量) |
---|---|---|---|
1 | 1 | 3 | 3 |
2 | 2 | 5 | 2.5 |
3 | 3 | 6 | 2 |
递推矩阵
列是前i个物品;行是j,当前背包容量。
j\i | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
1 | 0 | 3 | 3 | 3 |
2 | 0 | 3 | 5 | 5 |
3 | 0 | 3 | 8 | 8 |
4 | 0 | 3 | 8 | 9 |
5 | 0 | 3 | 8 | 11 |
def boolKnapsack(weight, value, capacity):
if len(weight) != len(value):
return 'Some items do not have weight or value!'
resMatrix = [[0 for i in range(len(weight) + 1)] for j in range(capacity + 1)]
for i in range(1, capacity + 1):
#注意这里i、j是行列号,j指示第几个物品,i说名当前背包容量
for j in range(1, len(weight) + 1):
if i < weight[j - 1]:
resMatrix[i][j] = resMatrix[i][j - 1]
else:
resMatrix[i][j] = max(resMatrix[i][j - 1], resMatrix[i - weight[j - 1]][j -1] + value[j - 1])
return resMatrix[capacity][len(weight)]