动态规划--钢条切割的C++实现

在《算法导论》动态规划那章,讲述了一个应用动态规划的例子--钢条切割。

钢条切割的问题是这样的:给定一段长度为n英寸的钢条和一个价格表(i=1,2,...,10),求切割钢条方案,使得销售收益最大。

我们可以采用一种简单的递归求解方法:我们将钢条从左边切割下长度为i的一段,只对右边剩下的 长度为n-i的一段继续进行切割(递归求解),对左边的一段则不再进行切割。用公式可以表示为:

方法一:带备忘的自顶向下法。

此方法按自然的递归形式编写过程,但过程会保存每个子问题的解,这里我们保存在数组r中。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省计算时间;否则,按通常形式计算这个子问题。我们称这个递归过程是带备忘的(memoized),因为它“记住”了之前已经计算的结果。代码如下:

#include<iostream>
using namespace std;

//对应于长度为1,2...10的钢条价格表
int p[10]={1,5,8,9,10,17,17,20,24,30};

//返回两个数的较大值
int max(int a,int b)
{
	return (a>b)?a:b;
}

/*首先检查所需值是否已知,如果是,则直接返回保存的值。
否则,用通常方法计算所需值q,然后将此值存入r[n]。值得注意的
是,笔者在此处没有对钢条的长度n进行限制,所以n可能大于10,
于是增加了自己的判断,若n大于10,则计算n-10的子序列*/
int MemoizedCutRodAux(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=0;i<n;i++)
		{
			if(i<10)
				q=max(q,p[i]+MemoizedCutRodAux(p,n-1-i,r));
			else
				q=max(q,r[10]+MemoizedCutRodAux(p,n-1-10,r));
		}
	}
	r[n]=q;
	return q;
}

int main()
{
	int n;
	cout<<"请输入钢条的长度:";
	cin>>n;
	//将辅助数组的r[0..n]元素均初始化为-1
	int *r=new int[n+1];
	for(int i=0;i<=n;i++)
	{
		r[i]=-1;
	}
	int sum=MemoizedCutRodAux(p,n,r);
	cout<<"最大收益为:"<<sum<<endl;
	return 0;
}
运行结果:

方法二:自底向上法

这种方法一般需要恰当定义子问题规模的概念,使得任何子问题的求解都只依赖于“更小的”子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们求解它时,它的所有前提子问题都已经求解完成。

#include<iostream>
using namespace std;

//对应于长度为1,2...10的钢条价格表
int p[10]={1,5,8,9,10,17,17,20,24,30};

//返回两个数的较大值
int max(int a,int b)
{
	return (a>b)?a:b;
}

/*首先创建一个新数组r来保存子问题的解,然后将r[0]初始化为0,因为长度为0的钢条没有收益
接着对j=1,2,...,n按升序求解每个规模为j的子问题。将规模为j的子问题的解存入r[j]。最后返回r[n],即最优解*/
int BottomUpCutRod(int p[],int n)
{
	int *r=new int[n+1];
	r[0]=0;
	for(int j=1;j<=n;j++)
	{
		int q=-1;
		for(int i=0;i<=j;i++)
		{
			if(i<10)
				q=max(q,p[i]+r[j-i-1]);
			else
				q=max(q,r[10]+r[j-10-1]);
		}
		r[j]=q;
	}
	return r[n];
}

int main()
{
	int n;
	cout<<"请输入钢条的长度:";
	cin>>n;
	int sum=BottomUpCutRod(p,n);
	cout<<"最大收益为:"<<sum<<endl;
	return 0;
}

运行结果为:



  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值