【演算法導論】動態規劃切鋼條
阿新 • • 發佈:2018-12-29
問題:鋼條切割
給定長度為n英寸的鋼條,和一個價格表P{1....n},求切割鋼條的方案,使得收益R最大。如果鋼條價格足夠大,可以完全不用切割。
來源:演算法導論,第15章
方法:1、遞迴窮舉;2、動態規劃
思路:
遞迴窮舉:鋼條分為兩部分左邊為不切割部分範圍長度j,右邊為切割部分範圍n-j,收益Max{R = P[j]+R[n-j]}
private static int findCutRod(int[]p,int n) { if(n==0){ return 0; } int q=Integer.MIN_VALUE; for(int j=1;j<=n;j++){ q =Math.max(q, p[j-1]+findCutRod(p, n-j)); } return q; }
缺點:當n增加,執行時間會爆炸式增長(畫個遞迴樹,非常明白就看出來了),原因是,重複的計算了很多次
優點:不需要藉助額外的空間
動態規劃:思路與遞迴相同,
不同的是:建立一個數組儲存已經計算過的切割次數對應的收益,直接查表,避免重複計算
下面是自頂向下的程式,為了方便陣列從1開始為有效值
private static int findCutRod_Memoized(int[] p, int n, int[] r) {//r是一個長度為n+1的陣列,原始元素小於0 int q = Integer.MIN_VALUE; if (r[n] >= 0) { //如果已經計算過r[n],直接用 return r[n]; } if (n == 0) { q = 0; } else { for (int i = 1; i <= n; i++) { q = Math.max(q, p[i] + findCutRod_Memoized(p, n - i, r)); } } r[n]=q; return q; }
這是自底向上的程式,因為用了陣列r來裝切割的結果,所以可以直接從底向上計算,
// 為了方便,陣列下標從1開始為有效值 // 自底向上計算,從R[1]算到R[n] private static int findCutRod_Memoized(int[] p, int n) { int[] r = new int[n + 1];//r陣列用來儲存收益 r[0] = 0;// 0為不切割情況 for (int i = 1; i <= n; i++) { int q = Integer.MIN_VALUE; for (int j = 1; j <= i; j++) { q = Math.max(q, p[j] + r[i - j]); } r[i] = q; } return r[n]; }
優點:避免了重複計算,相對於直接遞迴減少了計算量
缺點:需要佔用額外的空間存放結果陣列