動態規劃0-1揹包

LingLee_荊棘鳥發表於2017-07-18

1.

(1)動態規劃將問題劃分成子問題,在合併的結果。子問題是彼此獨立的。

(2)動態規劃將已計算過的子問題結果儲存到表中,是一種以空間換時間的方式。

(3)動態規劃的初始化條件注意,不要寫錯。找出遞迴函式關係式

(4)與記憶搜尋相比,動態規劃的計算具有一定的順序性,比如b的結果依賴a的結果,要先a在b,而記憶搜尋哪個先來就先計算哪個,將其結果儲存即可。



2、常見的動態規劃問題:

零一揹包問題

最長公共子序列LCS問題

最長升序子序列LIS問題

零錢問題

臺階問題

Floyd演算法



3.0-1揹包:

題目:一個揹包有一定的承重cap,有N件物品,每件都有自己的價值,記錄在陣列v中,也都有自己的重量,記錄在陣列w中,每件物品只能選擇要裝入揹包還是不裝入揹包,要求在不超過揹包承重的前提下,選出物品的總價值最大。給定物品的重量w價值v及物品數n和承重cap。請返回最大總價值。

[1,2,3],[1,2,3],3,6返回:6

思路:

N件物品,每一件有自己的重量和自己的價值,分別用一個陣列給出,揹包有一個限制總重量為cap,要求在滿足重量不超過cap的前提下,向背包中裝入物品,使得總的價值最大,求出最大價值


dp[i][j],即使用前i個物品,在重量不超過j時的最大價值為dp[i][j],注意對於重量,由於只給定了最大的範圍cap,但是在分解問題時應該將其從0開始列舉,即列舉重量從0開始直到cap,總共有cap+1個值,同理對於找零錢問題,只給定了一個最大值aim,但是在分解為題時應該從0開始直到aim考慮aim+1個值。

初始條件:

第一行的值,表示使用第一將物品w[0]來裝包,顯然當容量小於w[0]時無法裝入因此價值為0,之後可以裝入,於是最大價值顯然就是v[0]=4;

第一列的值,表示使用前i個物品來裝包,當容量為0時的最大價值,顯然一個都放不下,所以價值都為0。

對於任意的dp[i][j]2種情況:第i件物品放還是不放:

(1)如果i物品不裝入,dp[i-1][j];

(2)如果i物品裝入,物品i的重量為w[i],物品i的價值為v[i],那麼即是求在重量限制j-w[i]條件下的最大價值,即轉變為求dp[i-1][j-w[i]],求的dp[i-1][j-w[i]]後,此時的價值為dp[i-1][j-w[i]]+v[i]

dp[i][j]=Math.max(dp[i-1][j];dp[i-1][j-w[i]]+v[i]);需要注意的是,在求dp[i-1][j-w[i]]時,對於有些j並不一定大於w[i],即j-w[i],此時表示無法裝入物品i,於是此時dp[i][j]應該直接等於dp[i-1][j];於是在求dp[i][j]時對j-w[i]進行一個if判斷然後使用Math.max判斷或者直接取dp[i-1][j]即可。

動態規劃4部曲:

①建立動態規劃二維陣列儲存答案dp[n][cap+1];

②計算第1行和第1列的元素值

第1行:從j=w[0]開始dp[0][j]=v[0];

第1列:dp[i][0]=0;

③從上到下,從左到右計算任意dp[i][j],根據[j-w[i]<0使用不同的計算邏輯

④返回結果,右下角的值dp[n-1][aim]就是所求的結果;

[java] view plain copy
 print?
  1. import java.util.*;  
  2. //01揹包問題:動態規劃4部曲  
  3. public class Backpack {  
  4.     public int maxValue(int[] w, int[] v, int n, int cap) {  
  5.         //特殊輸入  
  6.         if(w==null||v==null||n<=0||cap<=0return 0;  
  7.         //①建立動態規劃陣列存放答案dp[][]  
  8.         int[][] dp=new int[n][cap+1];  
  9.         //②計算dp[][]第1行值  
  10.         for(int i=w[0];i<=cap;i++){  
  11.             dp[0][i]=v[0];  
  12.         }  
  13.         //②計算dp[][]第1列的值:全是0,預設也是0,故無需操作  
  14.         //③從上到下,從左到右計算任意dp[i][j]  
  15.         for(int i=1;i<n;i++){  
  16.             for(int j=1;j<=cap;j++){  
  17.                 if(j-w[i]>=0){  
  18.                     dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);  
  19.                 }else{  
  20.                     dp[i][j]=dp[i-1][j];  
  21.                 }  
  22.             }  
  23.         }  
  24.         //④返回dp[][]右下角即為所求結果  
  25.         return dp[n-1][cap];  
  26.     }  
  27. }  

程式碼二:只使用1個陣列,


public class Backpack {
    public int maxValue(int[] w, int[] v, int n, int cap) {
        // dp[x][y]表示物品數量為x,重量不超過y時揹包中的總價值
        //兩種情況:1.將x物品不加入到揹包中,那麼前x-1件物品的總重量不應該超過y。dp[x][y] = dp[x-1][y]
                //2.將x物品加入到揹包中,那麼前x-1前物品的總重量不應該超過y-w(x),因此dp[x][y] = dp[x-1][y-w(x)]+v(x);
        int[] dp = new int[cap+1];
          
        for(int i=0;i<n;i++){//控制物品的數量
            for(int j=cap;j>=w[i];j--){//空揹包中不能超重
                dp[j] = dp[j]>=dp[j-w[i]]+v[i]?dp[j]:dp[j-w[i]]+v[i];//選取j加入書包與j不加入書包的較大值
            }
        }
          
        return dp[cap];//返回陣列的最後一位即是最大總價值
      
    }
}



[java] view plain copy
 print?
  1. import java.util.*;  
  2. public class Backpack {  
  3.     public int maxValue(int[] w, int[] v, int n, int cap) {  
  4.         // write code here  
  5.         int dp[] = new int[cap + 1];  
  6.         for(int i = 0; i < n; i++ ){  
  7.             for(int j = cap; j >= w[i]; j--){  
  8.                 dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);  
  9.             }  
  10.         }  
  11.         return dp[cap];  
  12.     }  
  13. }  


相關文章