動態規劃的基本思想:html
將一個問題分解爲子問題遞歸求解,且將中間結果保存以免反覆計算。通常常使用來求最優解,且最優解的局部也是最優的。求解過程產生多個決策序列,下一步老是依賴上一步的結果,自底向上的求解。
算法
動態規劃算法可分解成從先到後的4個步驟:數組
1. 描寫敘述一個最優解的結構,尋找子問題,對問題進行劃分。spa
2. 定義狀態。每每將和子問題相關的各個變量的一組取值定義爲一個狀態。某個狀態的值就是這個子問題的解(如有k個變量,通常用K維的數組存儲各個狀態下的解,並可根 據這個數組記錄打印求解過程。)。code
3. 找出狀態轉移方程。一般是從一個狀態到還有一個狀態時變量值改變。
orm
4.以「自底向上」的方式計算最優解的值。htm
5. 從已計算的信息中構建出最優解的路徑。(最優解是問題達到最優值的一組解)遞歸
當中步驟1~4是動態規劃求解問題的基礎,假設題目僅僅要求最優解的值,則步驟5可以省略。ci
揹包問題it
01揹包: 有N件物品和一個重量爲M的揹包。(每種物品均僅僅有一件)第i件物品的重量是w[i],價值是p[i]。求解將哪些物品裝入揹包可以使價值總和最大。
全然揹包: 有N種物品和一個重量爲M的揹包,每種物品都有無限件可用。第i種物品的重量是w[i],價值是p[i]。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包重量,且價值總和最大。
多重揹包: 有N種物品和一個重量爲M的揹包。第i種物品最多有n[i]件可用,每件重量是w[i],價值是p[i]。求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包重量,且價值總和最大。
01揹包問題:
這是最基礎的揹包問題,特色是:每種物品僅有一件,可以選擇放或不放。
用子問題定義狀態:即c[i][v]表示前i件物品恰放入一個重量爲m的揹包可以得到的最大價值。則其狀態轉移方程即是:
c[i][m]=max{c[i-1][m],c[i-1][m-w[i]]+p[i]}
這個方程很重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。因此有必要將它詳解一下:「將前i件物品放入重量爲m的揹包中」這個子問題,若僅僅考慮第i件物品的策略(放或不放),那麼就可以轉化爲一個僅僅牽扯前i-1件物品的問題。假設不放第i件物品,那麼問題就轉化爲「前i-1件物品放入容量爲v的揹包中」,價值爲c[i-1][m];假設放第i件物品,那麼問題就轉化爲「前i-1件物品放入剩下的重量爲m-w[i]的揹包中」,此時能得到的最大價值就是c[i-1][m-w[i]]再加上經過放入第i件物品得到的價值p[i]。測試數據:
10,3
3,4
4,5
5,6
c[i][j]數組保存了1,2,3號物品依次選擇後的最大價值.
這個最大價值是怎麼得來的呢?從揹包容量爲0開始,1號物品先試,0,1,2,的容量都不能放.因此置0,揹包容量爲3則裏面放4.這樣,這一排揹包容量爲4,5,6,....10的時候,最佳方案都是放4.假如1號物品放入揹包.則再看2號物品.當揹包容量爲3的時候,最佳方案仍是上一排的最價方案c爲4.而揹包容量爲5的時候,則最佳方案爲本身的重量5.揹包容量爲7的時候,很是顯然是5加上一個值了。加誰??很是顯然是7-4=3的時候.上一排 c3的最佳方案是4.因此。總的最佳方案是5+4爲9.這樣.一排一排推下去。最右下放的數據就是最大的價值了。(注意第3排的揹包容量爲7的時候,最佳方案不是自己的6.而是上一排的9.說明這時候3號物品沒有被選.選的是1,2號物品.因此得9.)
public class Pack01 { public int [][] pack(int m,int n,int w[],int p[]){ //c[i][v]表示前i件物品恰放入一個重量爲m的揹包可以得到的最大價值 int c[][]= new int[n+1][m+1]; for(int i = 0;i<n+1;i++) c[i][0]=0; for(int j = 0;j<m+1;j++) c[0][j]=0; // for(int i = 1;i<n+1;i++){ for(int j = 1;j<m+1;j++){ //當物品爲i件重量爲j時,假設第i件的重量(w[i-1])小於重量j時,c[i][j]爲下列兩種狀況之中的一個: //(1)物品i不放入揹包中,因此c[i][j]爲c[i-1][j]的值 //(2)物品i放入揹包中,則揹包剩餘重量爲j-w[i-1],因此c[i][j]爲c[i-1][j-w[i-1]]的值加上當前物品i的價值 if(w[i-1]<=j){ if(c[i-1][j]<(c[i-1][j-w[i-1]]+p[i-1])) c[i][j] = c[i-1][j-w[i-1]]+p[i-1]; else c[i][j] = c[i-1][j]; }else c[i][j] = c[i-1][j]; } } return c; } /** * 逆推法求出最優解 * @param c * @param w * @param m * @param n * @return */ public int[] printPack(int c[][],int w[],int m,int n){ int x[] = new int[n]; //從最後一個狀態記錄c[n][m]開始逆推 for(int i = n;i>0;i--){ //假設c[i][m]大於c[i-1][m],說明c[i][m]這個最優值中包括了w[i-1](注意這裏是i-1,因爲c數組長度是n+1) if(c[i][m]>c[i-1][m]){ x[i-1] = 1; m-=w[i-1]; } } for(int j = 0;j<n;j++) System.out.println(x[j]); return x; } public static void main(String args[]){ int m = 10; int n = 3; int w[]={3,4,5}; int p[]={4,5,6}; Pack01 pack = new Pack01(); int c[][] = pack.pack(m, n, w, p); pack.printPack(c, w, m,n); } }