動態規劃-01揹包問題
揹包問題是很經典的動態規劃問題,變種問題也很多,最基本的問題是01揹包問題
問題描述:
有n 個物品,它們有各自的體積和價值,現有給定容量的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?
i:物品 | 1 | 2 | 3 | 4 |
w:體積 | 2 | 3 | 4 | 5 |
v:價值 | 3 | 4 | 5 | 6 |
分析:
01揹包問題可以看做是物品的序列,其中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,但不一定放進去能達到最優
之所以放進去也不一定最優,因為上面矩陣儲存的都是當前狀態最優的時候,剛能放進去的時候表示的是隻有物品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)]