动态规划 - 钢条切割问题

已知钢条切割的不同长度对应的不同价格如下所示:


长度i      1    2    3    4    5    6    7    8    9    10


价格pi    1    5    8    9    10   17   17    20    24    30


求输入长度,输出最佳的收益。


详细理论知识见《算法导论第十五章》 P359


书中给出三个算法:


一、自顶向下递归实现

缺点:当n足够大时,时间会爆炸性地增长。


伪代码:

CUT-ROD(p,n)
if n==0
   return 0
q=-MAX
for i = 1 to n
    q=max(q,p[i]+CUT_ROD(p,n-i))
return q

其中q初始化为负无穷大(或者任意负数也行)

p为一维数组代表各种长度的对应最佳方案


C++实现代码:

#include <iostream> //自顶向下递归实现
using namespace std;
int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
int CUT_ROD(int p[],int n){
	if (n == 0)
		return 0;
	int q = -1; //起始赋值为负数
	for (int i = 1; i <= n; i++){ //for i = 1 to n
		if (q < p[i-1] + CUT_ROD(p, n - i))  //输入为p[1…n]
			q = p[i-1] + CUT_ROD(p, n - i);
	}
	return q;
}
int main()
{
	int n;
	while (1)
	{

cin >> n;
		int res = CUT_ROD(p, n);
		cout << res << endl;
	}
}


二、带备忘的自顶向下法

引入备忘机制,递归解决问题,保存每个子问题的解。


伪代码:

MEMOIZED-CUT-ROD(p,n)
  let r[0...n]be a new array
for i = 0 to n
  r[i] = -MAX
return MEMOIZED-CUT-ROD-AUX(p,n,r)

MEMOIZED-CUT-ROD-AUX(p,n,r)
  if r[n]>=0
    return r[n]
if n==0
    q=0
else
    q=-MAX
      for i = 1 to n
          q = max(q,p[i]+MEMOIZED-CUT-ROD-AUX(p,n-i,r))

r[n]=q
return q

和自底向下的递归实现类似,仅仅是多提供了一个数组作为存储。效率提高。


C++实现


#include <iostream> //自顶向下递归实现
using namespace std;
int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
int r[10];
int n;

int MEMOIZED_CUT_ROD_AUX(int p[], int n, int r[]){
	int q;
	if (r[n] >= 0)
		return r[n];
	if (n == 0)
		q = 0;
	else
		q = -1;
	for (int i = 1; i <= n; i++)
	{
		if (q < p[i-1]+MEMOIZED_CUT_ROD_AUX(p, n - i, r))
			q = p[i - 1] + MEMOIZED_CUT_ROD_AUX(p, n - i, r);
	}
	r[n] = q;
	return q;
}
int MEMOIZED_CUT_ROD(int p[], int n){
	memset(r, -1, sizeof(r));
	return MEMOIZED_CUT_ROD_AUX(p, n, r);
}
int main()
{
	int n;
	while (1)
	{

        cin >> n;
		int res = MEMOIZED_CUT_ROD(p, n);
		cout << res << endl;
	}
}


三、自底向上法


自底向上依赖于“更小的”子问题求解。用两个循环代替递归实现,每次所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次。


伪代码:


BOTTOM-UP-CUT-ROD(p,n)
  let r[0..n]be a new array
  r[0]=0
  for j = 1 to n
      q = -MAX
      for i = 1 to j
             q = max(q,p[i]+r[j-i])
      r[j]=q;
return r[n]

C++实现代码:


#include <iostream> //自顶向下递归实现
using namespace std;
int p[10] = { 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
int r[10];
int n;
int BOTTOM_UP_CUT_ROD(int p[],int n){
	r[0] = 0;
	for (int j = 1; j <= n; j++){
		int q = -1;
		for (int i = 1; i <= j; i++)
		{
			if (q < p[i - 1] + r[j - i])
				q = p[i - 1] + r[j - i];
		}
		r[j] = q;
	}
	return r[n];
}
int main()
{
	int n;
	while (1)
	{

        cin >> n;
		int res = BOTTOM_UP_CUT_ROD(p,n);
		cout << res << endl;
	}
}

附上自底向下重构解(返回最优值以及最优值第一段长度)伪代码


MEMOIZED-CUT-ROD-AUX(p,n,r)
let r[0..n] and s[0..n] be new arrays
r[0]=0
for j=1 to n
    q = -1
    for i=1 to j
        if q < p[i]+r[j-i]
               q = p[i]+r[j-i]
                s[j]= i ;
 r[j] = -MAX

return MEMOIZED-CUT-ROD-AUX(p,n,r)


总结:

一直不理解动态规划,但是却在很多OJ上看到他的影子,今天在算法导论上算是收获颇丰,不过DP依旧是一道跨不过去但非得跨过去的障碍。

加油!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值