1. 程式人生 > >動態規劃:鋼條切割問題

動態規劃:鋼條切割問題

一、題目

鋼條切割問題 是《演算法導論》一書中介紹動態規劃時的一道引題。即:

某公司購買長鋼條,將其切割為短鋼條出售。假設切割工序沒有成本支出,已知長度為 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

種不同的切割方案,在 n 較大時,方案的數量級將急劇增大。因此我們需要考慮使用一種更好的策略來解決這個問題。

動態規劃 是典型的使用空間換取時間的一種方法,通過先求解子問題,最終解決問題。

例如,我們先求解出長度為 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。