鋼條切割問題求解方法及相關思考


鋼條切割問題求解方法及相關思考

題目來源於《算法導論》第15章第一節。問題如下:

給定一個長度為n英寸的鋼條和一個價格表pi(i=1,2,3,...n),求能夠使銷售收益rn最大的切割方案。

問題1:一共有多少種切割方式?

思路一:對於一個長度為n英寸的鋼條,其中一共有n-1個節點可供切割,在每一個節點處都可以選擇切割或者不切割,將對一根鋼條的切割過程視為從第一個節點直到第n-1個節點逐一選擇切割或者不切割的一個過程,利用乘法原理,可以算出來總共有2n-1種切割方案。以四個節點的鋼條為例:

思路二:也可以將切割一個長度為n英寸的鋼條視作是從n-1個節點當中選擇i(i=0,1,2,....,n-1)個節點進行分割的過程。那么總的分割方式m的計算方式可以使用排列組合的相關知識進行運算。m=C0n-1+C1n-1+.....+Cn-1n-1,根據牛頓二項式公式m=2n-1

問題解決方案

一、自頂向下遞歸

從鋼條左邊切割下長度為i的一段(不作任何處理),再對剩下的長度為n-i的鋼條進行切割(遞歸),一個最優的解決方案就是:rn=max(pi+rn-i)(1<=i<=n),C++代碼實現如下:

 1 #include "stdafx.h"
 2 int CutRod(int p[], int n)//輸入分別為價格數組和問題規模(鋼條大小)
 3 {
 4     if (n == 0)
 5     {
 6         return 0;
 7     }
 8     int q = -1000;//保證程序的順利啟動
 9     for (int i = 1; i <= n; i++)
10     {
11         if (q < p[i - 1] + CutRod(p, n - i))
12         {
13             q=p[i - 1] + CutRod(p, n - i);
14         }
15     }
16     return q;
17 }
18 int _tmain(int argc, _TCHAR* argv[])
19 {
20     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//鋼條分割的價格數組
21     printf("%d", CutRod(a, 10));
22     getchar();
23     return 0;
24 }

 

問題2:這樣的一種遞歸遍歷能否遍歷所有的2n-1種切割方法?

我之前一直以為這種計算方法無法遍歷到所有的切割方式,以為這種每一次都不考慮左半邊分割的方式會忽略掉某些解。但是細想的話會發現,原來對於左半邊的分割在之前的遍歷當中已經考慮到了,並不需要再考慮。比如,如果從距離鋼條左邊2英寸處分割成兩半然后只考慮右邊的n-2英寸的鋼條的分割的話,不需要考慮將左邊2英寸的鋼條再分為兩個1英寸的情況,因為在計算將左邊分為一個1英寸這種情況的時,另外的n-1部分其中有一種情況就是將其分為一個1寸和n-2寸的情況,這樣就考慮了之前所說的那種情況。也可以對比問題一當中的例子來思考這一問題。

二、利用動態規划的思想解決這一問題

上面所提到的那種算法會在遍歷的過程當中多次計算同樣的子問題,所以需要應用一些策略來減少算法計算數量,下面分別提供了兩種更好的方案。

帶備忘的自頂向下的解決方案:記錄在遍歷的過程當中所遇到的子問題的解,算法在后面的運行當中會對照所記錄的解,如果當前子問題存在解就直接輸出解,否則就執行運算;

自底向上的解決方案:先解決子問題,再利用子問題的解來解決父問題;

1、帶備忘的自頂向下的解決方案C++實現

此程序還可以記錄下每一個子問題的最佳切割方案,最后輸出問題整個問題的最佳切割過程。

 1 #include "stdafx.h"
 2 #include <string>
 3 using namespace std;
 4 struct A{
 5     int q;
 6     int* r;
 7     int* step;
 8 };
 9 //有備忘的自頂向下解決方案
10 //對規模為n的問題進行計算
11 int MCutRodA(int a[], int n, int r[],int step[])
12 {
13     int q;//問題的解
14     int s;//切割方案
15     if (r[n] >= 0)//如果子問題已經有解就返回所記錄的子問題的解
16     {
17         return r[n];
18     }
19     if (n == 0)
20     {
21         q = 0;
22         s = 0;
23     }
24     else{
25         q = -100;
26         for (int i = 1; i <= n; i++)
27         {
28             if (q < MCutRodA(a, n - i, r,step) + a[i-1])
29             {
30                 q = MCutRodA(a, n - i, r, step) + a[i - 1];
31                 s = i;
32             }
33         }
34     }
35     r[n] = q;//記錄解
36     step[n] = s;//記錄切割方案
37     return q;
38 }
39 //帶備忘的遞歸解決方案
40 A MCutRod(int a[],int n)
41 {
42     int* r = (int*)malloc((n + 1)*sizeof(int));//設置一個數組記錄不同規模的子問題的解
43     int* step = (int*)malloc((n + 1)*sizeof(int));//設置一個數組記錄不同規模子問題的切割方案
44     for (int i = 0; i <= n; i++)
45     {
46         r[i] = -1;
47     }
48     A B;
49     B.q = MCutRodA(a, n, r, step);
50     B.r = r;
51     B.step = step;
52     return B;
53 }
54 int _tmain(int argc, _TCHAR* argv[])
55 {
56     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//鋼條分割的價格數組
57     A B = MCutRod(a, 10);
58     printf("不同子問題的最大價值:");
59     for (int i = 0; i < 11; i++)
60     {
61         printf("%d ", B.r[i]);
62     }
63     printf("\n不同規模子問題從左邊開始的最優切割方式:");
64     for (int i = 0; i < 11; i++)
65     {
66         printf("%d ", B.step[i]);
67     }
68     printf("\n");
69     printf("%d", B.q);
70     int n = 10;
71     printf("最佳的切割方式為:");
72     while (n > 0)
73     {
74         printf("%d ", B.step[n]);
75         n = n - B.step[n];
76     }
77     getchar();
78     return 0;
79 }

 

2、自底向上解決方案的C++實現

 1 int BTUCutRod(int a[], int n)
 2 {
 3     int* r = (int*)malloc((n + 1)*sizeof(int));
 4     r[0] = 0;
 5     for (int i = 1; i <= n; i++)//遍歷所有可能的問題規模數
 6     {
 7         int q = -100;
 8         for (int j = 1; j <= i; j++)
 9         {
10             if ((a[j - 1] + r[i - j]) > q)
11             {
12                 q = a[j - 1] + r[i - j];
13             }
14         }
15         r[i] = q;
16     }
17     return r[n];
18 }
19 int _tmain(int argc, _TCHAR* argv[])
20 {
21     int a[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 25 };//鋼條分割的價格數組
22     printf("%d", BTUCutRod(a, 10));
23     getchar();
24     return 0;
25 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM