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

動態規劃-01揹包問題

揹包問題是很經典的動態規劃問題,變種問題也很多,最基本的問題是01揹包問題

問題描述:

有n 個物品,它們有各自的體積和價值,現有給定容量的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?

i:物品 1 2 3 4
w:體積 2 3 4 5
v:價值 3 4 5 6

分析:

01揹包問題可以看做是物品的序列X=[x_1,x_2,x_3,...x_i],其中x_i=[0,1],表示第i個物品放或者不放。狀態轉移矩陣是一個二維矩陣,其中行表示物品,列表示揹包容量。和之前講最長公共子序列一樣,考慮揹包裝完後最優的時候,那麼拿出最後一件後將揹包容量縮小還是最優的。

狀態轉移公式有如下性質

1. 初始狀態i=0 j=0,表示揹包容量為0,沒有物品,此時價值是0

2. 放入的時候有兩種條件:

    (1) 當w(i)>j,此時放不進去物品i,價值V(i,j)=V(i-1,j)

    (2) 當w(i)<=j,此時能放進物品i,但不一定放進去能達到最優

         V(i,j)=max(V[i-1,j],V[i-1,j-w(i)]+V(i))

         之所以放進去也不一定最優,因為上面矩陣儲存的都是當前狀態最優的時候,剛能放進去的時候表示的是隻有物品i的時候能放進去j中,但是拿出來了之前放的所有的東西,不一定就是最優了,所以有兩個分支。不放進去就是i-1時的狀態,放進去了要找i-1的時候揹包空間是j-w(i),再放i的時候正好揹包滿了這時候最優,看兩者哪個更大。

實現

w=[2,3,4,5]
v=[3,4,5,6]
bag=8
c=[[0 for i in range(bag+1)] for j in range(len(w)+1)]

for i in range(1,len(w)+1):
    for j in range(1,bag+1):
        if w[i-1]>j:  #放不下i,不放
            c[i][j]=c[i-1][j]
        else:   #放得下i,但放入不一定最優
            c[i][j]=max(c[i-1][j],c[i-1][j-w[i-1]]+v[i-1])
print(c[len(w)][bag])

 觀察之前的狀態轉移方程和上面的程式碼,可以發現每一次更新狀態V[i][j]的時候只用到了V[I-1]這一層,所以狀態轉移矩陣可以精簡為一個一維陣列,在更新V[i][j]的時候此時陣列的狀態是V[i-1]。但是我們要從後往前更新陣列,因為用到了V[i-1]。

b=[0 for i in range(bag+1)]
for i in range(len(w)):
    for j in range(bag+1)[::-1]:
        if w[i]<=j:
            b[j]=max(b[j],b[j-w[i]]+v[i])
print(b)

如果我們想獲得裝了哪些物品那麼只能用二維矩陣回溯,回溯的過程是:

從右下角開始,如果V[i][j]==V[i-1][j],說明物品i沒有放,回溯到V[i-1][j]

如果不等於,說明物品i放了,回溯到V[i-1][j-w(i)]

參考