// 最優原則:無論前面的策略如何,此後的決策是是基於當前狀態(由上一次決策產生)的最優決策。
// 當最優決策序列中包含最優決策子序列時,可創建動態規劃遞歸方法。
// (有些問題的遞歸式不必定能保證最優原則,所以在求解時有必要對它進行驗證。若不能保持最優原則,則不可應用動態規劃方法。)
// 在獲得最優解的遞歸式以後,須要執行回溯以構造最優解。
// 缺點:若是不努力地去避免重複計算,遞歸程序的複雜性將很是可觀。
// 方案:若是在遞歸程序中解決了重複計算問題時,複雜性將急劇降低。
// 遞歸方程也可用迭代方程來求解,這很天然地避免了重複計算。迭代方程雖然具備相同的複雜性,不須要附加的遞歸棧空間,所以更快一些。html
參考博客:算法
二、0-1揹包問題--動態規劃算法spa
三、揹包問題 遞歸方法.net
問題描述:
給定n種物品和一揹包,物品i的重量是wi,其價值爲vi,揹包的容量爲C。問應如何選擇裝入揹包的物品(物品不能分割),使得裝入揹包中物品的總價值最大?code
有編號分別爲a,b,c,d,e的五件物品,它們的重量分別是6,4,3,5,7,它們的價值分別是3,7,6,6,5,如今給你個承重爲10的揹包,如何讓揹包裏裝入的物品具備最大的價值總和?htm
採用填表法blog
名稱 | weight | value | 0 | 1 | 2 | 3 | 4遞歸 |
5 | 6 | 7 | 8 | 9 | 10 |
a | 6 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | 3 | 3 | 3 |
b | 4 | 7 | 0 | 0 | 0 | 0 | 7 | 7 | 7 | 7 | 7 | 7 | 10 |
c | 3 | 6 | 0 | 0 | 0 | 6 | 7 | 7 | 7 | 13 | 13 | 13 | 13 |
d | 5 | 6 | 0 | 0 | 0 | 6 | 7 | 7 | 7 | 13 | 13 | 13 | 13 |
e | 7 | 5 | 0 | 0 | 0 | 6 | 7 | 7 | 7 | 13 | 13 | 13 | 13 |
只要你能經過找規律手工填寫出上面這張表就算理解了01揹包的動態規劃算法。索引
首先要明確這張表是從上往下,從左到右生成的。(****這裏和參考的那篇文章正好相反)
這張表的填寫的規律:
一、先填寫第0行的數據 if(load>w[i]) f(0,j)=v[i] else f(0,j)=0;
二、從上往下依次填寫,f(i,j)=max{f(i-1,j-w[i])+v[i] , f(i-1,j)}
好比c10 (第c行第10列) f(2,10)= max {f(1,10) , f(1,10-6)+6} -> max{10 , 7+6} = 13 。
三、最大總價值天然就是 f(n-1,c)。
public static void main(String[] args) { int[] weights={6,4,3,5,7}; int[] values={3,7,6,6,5}; boolean [] selected=new boolean[weights.length]; System.out.println(getMaxReverse(weights, values, weights.length, 10)); System.out.println(getMax(selected, weights, values, 1, 10)); System.out.println(getMax(weights, values, 10)); } /** * 揹包問題第一步:得到動態公式 * f(i,j)=Max{f(i-1,j-Mi)+Pi,f(i-1,j)} * f(i,j)表示前i件物品放在承重j的揹包中的最大價值 * Mi是第i件物品的重量,Pi是第i件物品的價值 */ public static int getMaxReverse(int [] weights,int[] values,int index,int load) { if(index==0 || load==0) return 0; if(weights[index-1]>load)//不選擇當前 { return getMaxReverse(weights, values, index-1, load); } else { //選擇當前 int m1=getMaxReverse(weights, values, index-1, load-weights[index-1])+values[index-1]; //不選擇當前 int m2=getMaxReverse(weights, values, index-1, load); return Math.max(m1, m2); } }
//在遞歸裏面肯定裝了哪些物品不靠譜 public static int getMax(boolean[] selected,int [] weights,int[] values,int index,int load) { if(index==weights.length) return weights[index-1]>load?0:values[index-1]; if(weights[index-1]>load) { selected[index-1]=false; return getMax(selected, weights, values, index+1, load); } else { int v1=getMax(selected, weights, values, index+1, load); int v2=getMax(selected, weights, values, index+1, load-weights[index-1])+values[index-1]; if(v1>v2) { selected[index-1]=false; return v1; } else { selected[index-1]=true; return v2; } } } /**
非遞歸解法
*/ public static int getMax(int [] weights,int[] values,int load) { //新建數組用於存放數據 //load+1的做用是取索引方便 int len=weights.length; int[][] maxSum=new int[len][load+1]; //第一步初始化第0行 for(int i=0;i<load+1;i++) { maxSum[0][i]=weights[0]>i?0:values[0]; } //從第1行到第n-1行 for(int i=1;i<len;i++) { for(int j=0;j<load+1;j++) { if(j>=weights[i]) maxSum[i][j]=Math.max(maxSum[i-1][j], maxSum[i-1][j-weights[i]]+values[i]); else maxSum[i][j]=maxSum[i-1][j]; } } for(int i=0;i<len;i++) System.out.println(Arrays.toString(maxSum[i])); //輸出選擇的物品 int temp=load; for(int i=len-1;i>0;i--) { if(maxSum[i][temp]!=maxSum[i-1][temp]) { System.out.println("取了第"+(i+1)+"件物品 重量"+weights[i]+" 價值"+values[i]); temp-=weights[i]; } } //返回 return maxSum[len-1][load]; }