动态规划之钢条切割问题

假设公司出售一段长度为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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值