动态规划----切割钢条

动态规划方法通常用来求解最优化问题。动态规划算法设计步骤:

  1. 刻画一个最优解的结构特征。
  2. 递归定义最优解的值。
  3. 计算最优解的值,通常采用自底向上的方法。
  4. 利用计算出的信息构造一个最优解。


带备忘的自顶向下法:此方法仍按自然的递归形式编写过程,但过程会保存每个子问题的解(通常保存在一个数组或散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间;否则,按通常方式计算这个子问题。


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


问题:公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切割方案。

假定我们知道公司出售一段长度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




我们将钢条从左边切割下长度为i的一段,只对右边剩下的n-i的一段继续进行切割,对左边的一段不再进行切割。


#include<iostream>
#include<algorithm>

using namespace std;

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 main()
{
	cout << "请输入钢条的长度(1<=n<=10):";
	int n;
	int p[11] = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
	while (cin >> n)
	{
		cout << "最大收益为:" << CUT_ROD(p, n) << endl;
		cout << "继续?(y/n):";
		char ch;
		cin >> ch;
		if (ch == 'y')
		{
			cout << "请输入钢条的长度(1<=n<=10):";
			continue;
		}
		else
			break;
	}
	cout << __DATE__ << "  " << __TIME__ << endl;
	system("pause");
	return 0;
}

运行结果:

请输入钢条的长度(1<=n<=10):10
最大收益为:30
继续?(y/n):y
请输入钢条的长度(1<=n<=10):9
最大收益为:25
继续?(y/n):y
请输入钢条的长度(1<=n<=10):8
最大收益为:22
继续?(y/n):n
Aug 24 2016  12:36:31
请按任意键继续. . .


在这个递归过程中,CUT_ROD会反复求解相同的子问题,因此当这个递归过程展开的时候,它的工作量会爆炸性的增长!对于长度为n的钢条,CUT_ROD显然考察了所有2^(n-1)中可能的切割方案!动态规划应该仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。


带备忘录的自顶而下的方法:

#include<iostream>
#include<algorithm>

using namespace std;

int CUT_ROD2(int *p, int n, int *r)
{
	if (r[n] != -1)
		return r[n];
	if (n == 0)
		return 0;
	int q = -1;
	for (int i = 1; i <= n; i++)
		q = max(q, p[i] + CUT_ROD2(p, n - i, r));
	r[n] = q;
	return q;
}

int main()
{
	cout << "请输入钢条的长度(1<=n<=10):";
	int n;
	int p[11] = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
	while (cin >> n)
	{
		int *r = new int[n+1];//备忘录
		memset(r, -1, sizeof(int)*(n + 1));
		cout << "最大收益为:" << CUT_ROD2(p, n, r) << endl;
		cout << "继续?(y/n):";
		char ch;
		cin >> ch;
		if (ch == 'y')
		{
			cout << "请输入钢条的长度(1<=n<=10):";
			continue;
		}
		else
			break;
		free(r);
	}
	cout << __DATE__ << "  " << __TIME__ << endl;
	system("pause");
	return 0;
}


运行结果和上一题一样!


自底向上版本:

#include<iostream>
#include<algorithm>

using namespace std;

int CUT_ROD3(int *p, int n, int *r)
{
	for (int i = 1; i <= n; ++i)
	{
		int q = -1;
		for (int j = 1; j <= i; ++j)
		{
			q = max(q, p[j] + r[i - j]);
		}
		r[i] = q;
	}
	return r[n];
}

int main()
{
	cout << "请输入钢条的长度(1<=n<=10):";
	int n;
	int p[11] = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
	while (cin >> n)
	{
		int *r = new int[n + 1];//备忘录
		memset(r, 0, sizeof(int)*(n + 1));
		cout << "最大收益为:" << CUT_ROD3(p, n, r) << endl;
		cout << "继续?(y/n):";
		char ch;
		cin >> ch;
		if (ch == 'y' || ch == 'Y')
		{
			cout << "请输入钢条的长度(1<=n<=10):";
			continue;
		}
		else
			break;
		free(r);
	}
	cout << __DATE__ << "  " << __TIME__ << endl;
	system("pause");
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值