動態規劃:鋼條切割問題
阿新 • • 發佈:2018-12-16
一、題目
鋼條切割問題 是《演算法導論》一書中介紹動態規劃時的一道引題。即:
某公司購買長鋼條,將其切割為短鋼條出售。假設切割工序沒有成本支出,已知長度為 i 的鋼條出售價格為 pi ,鋼條長度均為整數,公司管理希望知道最佳的切割方案。
價格表樣例:
長度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
價格pi | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 | 30 |
現給定長鋼條的長度 n ,以及長度 i 的鋼條售價為 pi,求鋼條切割方案,使得收益最大。
二、分析
首先最容易想到的一種解法就是,將長度為 n 的鋼條的切割方案全部羅列出來,然後取出其中一個收益最大的方案返回。
但是,對於長度為 n 的鋼條,將會有 2n-1
動態規劃 是典型的使用空間換取時間的一種方法,通過先求解子問題,最終解決問題。
例如,我們先求解出長度為 1 時的最優解,並將結果儲存起來;再求解長度為 2 時的最優解,並將結果儲存起來;……;最終求得長度為 n 時的最優解返回。在求解長度為 i
時的最優解時,我們需要依次比較長度為 k(1<k<i)
的最優解加上長度為 i-k
的最優解的和,最終選擇一個最大值作為長度為 i 時的最優解並儲存起來。
最終進行到 n 時,由於前面的最優解都已經得到了,所以我們也很容易得到 n 時的最優解。
三、解答
public class Main {
public static void main(String[] args) {
int[] p = new int[] { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 }; // 長度為陣列索引值,所對應的價值
int[][] result = cutRob(p, 10);
System.out.print("i:\t");
for (int i = 0; i <= 10; i++) {
System.out.print( i + "\t");
}
System.out.print("\nr[i]:\t");
for (int i = 0; i < result[0].length; i++) {
System.out.print(result[0][i] + "\t");
}
System.out.print("\ns[i]:\t");
for (int i = 0; i < result[0].length; i++) {
System.out.print(result[1][i] + "\t");
}
}
public static int[][] cutRob(int[] p, int n) {
// result[0] 用來儲存不同長度 n 時的最大價值
// result[1] 用來儲存不同長度 n 時的最大價值方案,第一段切割的長度
int[][] result = new int[2][n + 1];
for (int i = 1; i <= n; i++) {
int max = Integer.MIN_VALUE;
int index = 1;
while (index <= i && index < p.length) {
int current = p[index] + result[0][i - index];
if (current > max) {
max = current;
result[1][i] = index;
}
index++;
}
result[0][i] = max;
}
return result;
}
}
執行結果:
我們使用 i
表示鋼條的長度,用 r[i]
表示長度為 i 的鋼條對應的最大價值,用 s[i]
表示最大價值的方案,切割的第一段鋼條的長度,那麼結果為
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
r[i] | 0 | 1 | 5 | 8 | 10 | 13 | 17 | 18 | 22 | 25 | 30 |
s[i] | 0 | 1 | 2 | 3 | 2 | 2 | 6 | 1 | 2 | 3 | 10 |
這裡可以看到,假設長度為 9 的鋼條的最佳切割方案即是: 第一段切割長度 3,價值 8;切割後長度為 6,切割長度還是 6,價值為 17。最終切割方案為一段 3,一段 6,最大價值為 8 + 17 = 25。