演算法基礎四:動態規劃---0-1揹包問題
阿新 • • 發佈:2021-10-05
演算法基礎四:動態規劃---0-1揹包問題
一、演算法描述與分析
1、問題的理解與描述
問題理解
問題描述
2、解題思路
①思路
②狀態轉移方程
f(k,w):當揹包容量為w,現有k件物品可以偷所能偷到的最大價值。
③表格(圖示)
解釋:
- 第一行和第一列為0,因為當揹包容量為0的時候,不論還有幾件物品可以偷,那麼價值都是0,偷不到。如果剩下0件物品可以偷,那麼價值也是0。相當於初始化。
- 比如f(1,2),意思就是揹包容量還剩下2,現在還有一件物品可以偷,這個物品的重量為2,價值為3。或者帶入轉移方程也可以看出來,偷與不偷的情況。
二、演算法的虛擬碼描述
1.演算法虛擬碼
KNAPSACK(v, w, C) n <- length[v] for j <- 0 to C do f[0,j] <-0 #j控制容量 for i<-1 to n do f[i,0] <-0 for j<-1 to C do f[i,j] <- f[i-1,j] if wi <= j then if vi + f[i-1,j-wi] > f[i-1,j then f[i,j] <- vi + f[i-1,j-wi return f
2、構造一個最優解
利用KNAPSACK返回的矩陣m,可以構造出如下最優解:
BUILD-SOLUTION(f,w,C)
n <- length[w]
j <- C
for i<- n to 1
do if f[i,j] = f[i-1,j]
then x[i] <- 0
else
x[i] <- 1
j <- j-w[i]#如果拿了的話,容量減少
return x
三、程式碼實現
1、演算法程式碼
public class Knapsack { public static int[][] knapsack(int[] w,int[] v,int c){ int i,j,n = w.length; int [][] f = new int[n+1][c+1]; for (i=1;i<n+1;i++) f[i][0] = 0; for (j=0;j<c+1;j++) f[0][j] = 0; for (i=1;i<=n;i++) for (j=1;j<=c;j++){ f[i][j] = f[i-1][j];//預設其太重放不下。或者是不拿當前這個物品的情況。 if (w[i-1]<=j)//此種情況就是如果當前物品能放下 if (v[i-1]+f[i-1][j-w[i-1]]>f[i-1][j])//並且拿了這個物品的總價值要大於不拿這個物品 f[i][j] = v[i-1]+f[i-1][j-w[i-1]]; } return f; } public static int[] buildSolution(int[][] f,int[] w,int c){ int i,j=c,n=w.length; int[] x = new int[n]; for (i=n;i>=1;i--) if (f[i][j] == f[i-1][j])//說明當前這個物品,可能是沒拿,也有可能是放不下。 x[i-1] = 0;//那麼就說明這個物品沒拿 else {//否則就說拿了,需要減去這個重量,重新的去尋找下一個拿了的物品。 x[i-1] = 1; j -= w[i-1]; } return x; } }
2、測試程式碼
public class Test { public static void main(String[] args) { int w[] = {2,3,4,5},v[]={3,4,5,7}; int[][] f; int[] x; f = Knapsack.knapsack(w,v,9); x = Knapsack.buildSolution(f,w,9); for (int i = 0; i < 4; i++) { System.out.print(x[i]+" "); } System.out.println(); } }
四、思考求解動態規劃
1、組成部分一:確定狀態
①最後一步:
在本題中,最後一步,也就是從最後一個物品出發,最後一個物品,拿還是不拿。不拿的價值是多少,拿了的價值是多少。
②子問題:
前一個物品拿完之後,當前這個物品拿不拿,拿了之後價值和沒拿之後的價值。
2、組成部分二:轉移方程
列出轉移方程
3、初始條件和邊界情況
- 陣列要開多大?
- 如果重量為0,值該怎麼辦。
- 如果沒有物品可以拿,值該怎麼辦。
- 迴圈要多少次
4、計算順序
- 由於我們是從左到右的,一直到右下角,在計算當前的時候,會參考二維陣列的左面,左斜上方的格子裡 的值,自然我們的計算順序是從左到右,從上到下。