突然发现,钢条切割竟然没有写。
假设公司出售一段长度为i英寸的钢条的价格为Pi(i = 1, 2, ...单位:美元),下面给出了价格表样例:
长度i 1 2 3 4 5 6 7 8 9 10
价格Pi 1 5 8 9 10 17 17 20 24 30
切割钢条的问题是这样的:给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大。
当然,如果长度为n英寸的钢条价格Pn足够大,最优解可能就是完全不需要切割。
对于上述价格表样例,我们可以观察所有最优收益值Ri及对应的最优解方案:
R1 = 1,切割方案1 = 1(无切割)
R2 = 5,切割方案2 = 2(无切割)
R3 = 8, 切割方案3 = 3(无切割)
R4 = 10, 切割方案4 = 2 + 2
R5 = 13, 切割方案5 = 2 + 3
R6 = 17, 切割方案6 = 6(无切割)
R7 = 18, 切割方案7 = 1 + 6或7 = 2 + 2 + 3
R8 = 22, 切割方案8 = 2 + 6
R9 = 25, 切割方案9 = 3 + 6
R10 = 30,切割方案10 = 10(无切割)
更一般地,对于Rn(n >= 1),我们可以用更短的钢条的最优切割收益来描述它:
Rn = max(Pn, R1 + Rn-1, R2 + Rn-2,...,Rn-1 + R1)
首先将钢条切割为长度为i和n - i两段,接着求解这两段的最优切割收益Ri和Rn - i
(每种方案的最优收益为两段的最优收益之和),由于无法预知哪种方案会获得最优收益,
我们必须考察所有可能的i,选取其中收益最大者。如果直接出售原钢条会获得最大收益,
我们当然可以选择不做任何切割。
分析到这里,假设现在出售8英寸的钢条,应该怎么切割呢?
下面给出三种方法:
1、自顶向下;
2、带备忘的自顶向下
3、由底向上
其中由底向上最简单也最高效。大家可以尝试将钢管长度设大些,比较不同方法之间的运行速度差异。通过对比,可以加深对动态规划的思想的了解与记忆。
给出例子:对于自顶向下钢条收益按照上述要求,那么可以计算钢条长度在30以下。(效率很低)
对于带备忘的自顶向下,可以计算钢条长度在10000以内。
对于自底向上,可以计算钢条长度在30000以内。(上述范围仅供参考)
代码:
1 #include <bits/stdc++.h> 2 #define min_num -100010 3 #define max_size 100010 4 int p[max_size]; 5 int r[max_size]; 6 using namespace std; 7 //****************自顶向下递归实现*************** 8 double cut_rod(int p[],int n){ 9 if(n==0) return 0; 10 double q=min_num; 11 for(int i=1;i<=n;i++){ 12 q=max(q,p[i]+cut_rod(p,n-i)); 13 } 14 return q; 15 } 16 //****************带备忘的自顶向下******************// 17 int memoized_cut_rod_aux(int p[],int n,int r[]){ 18 int q=0; 19 if(r[n]>=0) 20 return r[n]; 21 if(n==0) 22 q=0; 23 else q=min_num; 24 for(int i=1;i<=n;i++){ 25 q=max(q,p[i]+memoized_cut_rod_aux(p,n-i,r)); 26 } 27 r[n]=q; 28 return q; 29 } 30 31 int memoized_cut_rod(int p[],int n){ 32 for(int i=0;i<=n;i++){ 33 r[i]=min_num; 34 } 35 return memoized_cut_rod_aux(p,n,r); 36 } 37 38 //**********自底向上的算法******// 39 int bottom_up_cut_rod(int p[],int n){ 40 for(int i=0;i<=n;i++){ 41 r[i]=0; 42 } 43 int q; 44 for(int j=1;j<=n;j++){ 45 q=min_num; 46 for(int i=1;i<=j;i++){ 47 q=max(q,p[i]+r[j-i]); 48 } 49 r[j]=q; 50 } 51 return r[n]; 52 } 53 //**********测试函数************// 54 int main(){ 55 int num; 56 cout<<"请输入钢条收益num:"<<endl; 57 cin>>num; 58 for(int i=1;i<=num;i++){ 59 scanf("%d",&p[i]); 60 } 61 int n; 62 cout<<"请输入钢条长度: "; 63 while(~scanf("%d",&n)){ 64 //cout<<"最大收益是:"<<cut_rod(p,n)<<endl;//自顶向下的递归算法 65 //cout<<"最大收益是:"<<memoized_cut_rod(p,n)<<endl;//带备忘的自顶向下 66 cout<<"最大收益是:"<<bottom_up_cut_rod(p,n)<<endl;//自底向上的算法 67 cout<<"请输入钢条长度: "; 68 } 69 }