1. 程式人生 > >動態規劃 0-1揹包問題

動態規劃 0-1揹包問題

問題

給定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(i1,

j)
2) j>=w(i)V(i,j)=maxV(i1,j)V(i1,jw(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)]