動態規劃--0,1揹包問題(再也不怕類似揹包問題了)
阿新 • • 發佈:2019-12-08
這種型別問題三大要素:總重量、每件物品重量、每件物品價值,問最終能夠塞進揹包中的價值最大是多少?應該怎麼選擇物品?
當然也不一定是這些,例如上節所說的礦工挖礦:總人數、挖每座礦的人數、每座礦的金子數。
也就是說,只要出現了這三大要素,都可以視為0,1揹包問題(物品不可拆分)
動態規劃三要素:邊界、最優子結構、狀態轉移方程。
我們一步步進行解析:
初始化:物品總重量:c=8,物品類別:n=['a','b','c','d'],物品重量:w=[2,4,5,3],物品價值:v=[5,4,6,2]
假設我們目前只有一個物品a,
- 揹包的總重量為0,那麼我們獲得總價值為0
- 揹包的總重量為1,那麼我們獲得總價值為1
- 揹包的總重量為2,此時,正好可以放下物品a,因為它的重量正好是2,那麼我們獲得總價值為5
- 在這之後,我們可以獲得的總價值均為5,因為總重量>2,且只有a一個物品
假設我們現在多了一個物品b,
- 揹包總重量0,那麼我們獲得總價值為0
- 揹包總重量1,那麼我們獲得總價值為0
- 揹包總重量2,此時可以放進a,那麼我們獲得總價值為5
- 揹包總重量3,仍只能放進a,那麼我們獲得總價值為5
- 揹包總重量4,此時我們既可以放進a,也可以放進b,選價值最大的,也就是放進a,那麼我們獲得總價值為5
- 揹包總重量5,那麼我們獲得總價值為5
- 揹包總重量6,此時就可以放進a,b了,那麼我們獲得總價值為5+4=9
- 在這之後,我們可以獲得的總價值均為9
假設我們現在多了一個物品c
- 揹包總重量0,那麼我們獲得總價值為0
- 揹包總重量1,那麼我們獲得總價值為0
- 揹包總重量2,此時可以放進a,那麼我們獲得總價值為5
- 揹包總重量3,仍只能放進a,那麼我們獲得總價值為5
- 揹包總重量4,此時我們既可以放進a,也可以放進b,選價值最大的,也就是放進a,那麼我們獲得總價值為5
- 揹包總重量5,此時我們可以放a,也可以放c,選最大的,也就是放進c,此時我們獲得總價值為6
- 揹包總重量6,此時可以放進a,b了,也可以只放進c,選最大的,那麼我們獲得總價值為5+4=9>6
- 在這之後,我們可以獲得的總價值均為9
依此類推下去,看起來挺複雜,其實是有套路的,那我們應該如何實現。
對付這種問題,一般就直接初始化一個數組:dp[len(n)+1][c+1],即5行9列的二維陣列(行代表物品種類,列代表總重量,多加一列和一行是為了更容易理解)
接下來,我們就從程式碼中一步步剖析:
n=['a','b','b','d'] c=8 w=[2,4,5,3] v=[5,4,6,2] def bag(c,w,v): #初始化陣列,dp[i][j]表示總重量為j,物品種類為i,可以獲得的最大價值 dp = [[0 for _ in range(c+1)] for _ in range(len(w)+1)] #定義邊界,也就是當我們只有物品a,總重量依次由0-8 #也就是第一步我們所解釋的 for i in range(1,c+1): if i>=w[0]: dp[1][i] = v[0] #遍歷從第二行第一列開始 for i in range(2,len(w)+1): for j in range(1,c+1): #如果對於第i個物品,當前總重量放不下它,那麼獲得的最大值就是放下之前的i-1個 if j<w[i-1]: dp[i][j] = dp[i-1][j] #如果放得下,那麼獲得的最大值就是max(放下之前的i-1個,第i個物品的價值+ # (總重量-第i個物品的重量)在前i-1個物品的值) #注意下標,第i個物品的重量是w[i-1],價值是v[i-1] else: dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i-1]]+v[i-1]) return dp def show(c,w,dp): print("最大價值為:",dp[len(w)][c]) x = [False for _ in range(len(w)+1)] j = c #8 i = len(w) #4 while i>=0: if dp[i][j]>dp[i-1][j]: x[i]=True j=j-w[i-1] i-=1 print("選擇的物品是:") for i in range(len(w)+1): if x[i]: print("第",i,"個",end='') print('') dp = bag(c,w,v) for i in range(len(w)+1): print(dp[i]) show(c,w,dp)
執行結果:
最後在輸出第幾個物品的時候採用由下往上,如果下面的值大於上面的值,說明這個物品被放置了,然後總重量減去該物品重量,繼續判斷,如藍色所標記的。
總結:揹包問題三步走:
(1)初始化dp陣列,行為物品個數+1,列為總重量+1
(2)初始化邊界,只放一個物品,在不同總重量下得到的價值
(3)遍歷陣列,依賴dp[i-1]更新d