假设公司出售一段长度为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英寸的钢条,应该怎么切割呢?
注意到,为了求解规模为n的原问题,我们先求解形式完全一样,但规模更小的子问题。即当完成首次切割后,我们将两段钢条看成两个独立的钢条切割问题实例。我们通过组合两个相关子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大者,构成原问题的最优解。我们称钢条切割问题满足最优子结构(optimal substructure)性质:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<iterator>
using namespace std;
int Cut_Rod(int *p,int n); //自顶向下递归实现声明
int Memoized_Cut_Rod(int *p,int n);//
int Memoized_Cut_Rod_Aux(int *p,int n,int *r);//
int Bottom_Up_Cut_Rod(int *p,int n);//
pair<vector<int>,vector<int> > Extended_Bottom_Up_Cut_Rod(int *p,int n);//
void Print_Cut_Rod_Solution(vector<int> p,int n);//
int Cut_Rod(int *p,int n){
if(n==0)
return 0;
int q=-1;
for(int i=1;i<=n;i++){
q=max(q,p[i]+Cut_Rod(p,n-i));
}
return q;
}
int Memoized_Cut_Rod(int *p,int n){
int r[11]={};
memset(r,-1,sizeof(r));
//for(int i=0;i<=n;i++)
// r[i]=-1;
int q=Memoized_Cut_Rod_Aux(p,n,r);
return q;
}
int Memoized_Cut_Rod_Aux(int *p,int n,int *r){
if(r[n]>=0)
return r[n];
int q;
if(n==0)
q=0;
else{
q=-1;
for(int i=1;i<=n;i++)
q=max(q,p[i]+Memoized_Cut_Rod_Aux(p,n-i,r));
}
r[n]=q;
return q;
}
int Bottom_Up_Cut_Rod(int *p,int n){
int r[11];
r[0]=0;
for(int j=1;j<=n;j++){
int q=-1;
for(int i=1;i<=j;i++)
q=max(q,p[i]+r[j-i]);
r[j]=q;
}
return r[n];
}
//r[]: 记录最优解
//s[]:记录最优解的第一段的钢条的切割长度
pair<vector<int>,vector<int> > Extended_Bottom_Up_Cut_Rod(vector<int> p,int n){
vector<int> r,s(p.size());
r.push_back(0);
for(int j=1;j<=n;j++){
int q=-1;
for(int i=1;i<=j;i++){
if(q<p[i]+r[j-i]){
q=p[i]+r[j-i];
s[j]=i;
}
}
r.push_back(q);
}
return make_pair(r,s);
}
void Print_Cut_Rod_Solution(vector<int> p,int n){
pair<vector<int>,vector<int> > vvp=Extended_Bottom_Up_Cut_Rod(p,n);
cout << vvp.first.at(n) << endl;
vector<int> s = vvp.second;
while (n > 0)
{
cout << s[n] << endl;
n -= s[n];
}
}
int main(){
int p[]={0,1,5,8,9,10,17,17,20,24,30};
int n;
cout<<"请输入n:n=";
while(cin>>n&&n){
cout<<"自顶向下递归实现:"<<Cut_Rod(p,n)<<endl;
cout<<"带备忘的自顶向下实现:"<<Memoized_Cut_Rod(p,n)<<endl;
cout<<"自底向上实现:"<<Bottom_Up_Cut_Rod(p,n)<<endl;
vector<int> p(p, p+sizeof(p)/sizeof(p[0]));
cout << "扩展的自底向下法:" << endl;
Print_Cut_Rod_Solution(p, n);
cout<<endl;
cout<<"请输入n:n=";
}
return 0;
}