有一個揹包,他的容量爲C(Capacity)。如今有n種不一樣的物品編號分別爲0、1....n-1。其中每一件物品的重量爲w(i),價值爲v(i)。問能夠向這個揹包中放入哪些物品,使得在不超過揹包容量的基礎上,揹包內物品價值最大。數組
每一件物品均可以放進揹包,也能夠不放進揹包。找出全部可能組合一共2^n種組合數據結構
時間複雜度:O((2^n)*n)函數
咱們首先使用遞歸函數自上而下進行思考。優化
明確兩點:spa
第1、遞歸函數的定義3d
第2、數據結構code
F(n,C)遞歸函數定義:將n個物品放入容量爲C的揹包,使得價值最大。視頻
這裏要注意一下,第二個參數必定是剩餘容量。咱們經過使用剩餘容量來控制價值。blog
F(i,c) = F(i-1,c)遞歸
= v(i) + F(i-1 , c-w(i))
狀態轉移方程:
F(i,c) = max( F(i-1 , c) , v(i) + F(i-1 , c-w(i) ) )
即,當前價值的最大值爲,不放入第i個物品(對應剩餘容量爲c)和放入第i個物品(對應剩餘容量爲C-w(i))兩種狀況的最大值。
借某盜版視頻中的一個例子:
咱們這裏選擇一個二維數組,來迭代記錄處理的結果。
這個二維數組dp[n][C] 其中n爲物品數量,C爲最大容量。
儲存的值dp[i][j]含義爲:考慮放入0~i 這些物品, 揹包容量爲j
咱們考慮放入第一個物品。
因爲第一個物品,編號爲0,重量爲1,價值爲2。
對於容量爲0的揹包,放不下該物品,因此該揹包價值爲0.
其他容量1~5,都可放下該物品。因此只考慮物品0,不一樣揹包大小對應的最大可能價值如圖。
第一行處理爲初始化,從第二行開始進行迭代。
第二行開始,就須要單獨處理。
考慮dp[1][0],揹包容量爲0,理所應當爲0
考慮dp[1][1],此處咱們依舊沒法放入物品1,因此咱們使用上一層的結果,即0~0物品在容量爲1揹包狀況的最大價值。
考慮dp[1][2],此處咱們終於能夠放下物品1了,因此咱們考慮若是要放下物品1,剩餘揹包最大的可能價值,即dp[0][0]
咱們對比上一層的狀況,以及掏空揹包放入物品2的狀況。發現最大值爲後者,因此dp[1][2]爲10
同上,咱們掏出能夠放下物品1的空間,考慮此時最大價值,即dp[0][1]。對比他和上一層dp[0][3]的大小,發現前者大。
故此時dp[1][3]爲dp[0][1]+v[1] = 16.
以此類推,咱們每次清空對應物品大小的揹包,而後放入對應物品,對比不放入物品的上一行。求出最大值
依次填入dp[][]得出最終的二維數組。
代碼以下
class Knapasack01{ public : int knapsack01(int[] w,int[] v,int C){ //w爲0-~n-1物品對應價值 //v爲0~n-1物品對應重量。 //C爲揹包容量 int n = w.length(); if(n == 0) return 0; //動態規劃記憶數組。 int[][] dp = new int[n][C]; //初始化第一行。 for(int j=0 ; i<= C ; j++) dp[0][j] = (j>=w[0]?v[0]:0); for(int i=1 ; i<n ; i++) for(int j=0 ; j<C ; j++){ dp[i][j] = dp[i-1][j]; if(j>=w[i]) memo[i][j] = (int)Math.max(dp[i][j] , v[i]+dp[i][j-w[i]]); } return dp[n-1][C]; } }