動態規劃 0-1揹包問題
問題
給定n個物品和一揹包,物品i的重量是wi,其價值是vi,揹包的容量是m,問如何選擇裝入揹包中的物品總價值最大?
解答思路
a) 把揹包問題抽象化(X1,X2,…,Xn,其中 Xi 取0或1,表示第 i 個物品選或不選,類似指示器隨機變數),Vi表示第 i 個物品的價值,Wi表示第 i 個物品的體積(重量);
b) 建立模型,即求
c) 約束條件,
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)
2)
顯然其具有重疊子問題。
易得反例,其不能使用貪心演算法,但是分數揹包可以。
時間複雜度是進行填表
舉例
輸入:
揹包承重: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)]