动态规划之钢条切割问题

钢条切割问题:

某公司购买长钢条,将其切割为短钢条,价格案例如下:

长度i12345678910
价格Pi1589101717202430

给定一个长度为n英寸的钢条,怎么切割使得销售收益最大。

最优子结构:问题的最优解由相关子问题的最优解组合而成,而这些子问题可以独立求解。


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

即问题分解的方式为:将长度为n的钢条分解为左边开始一段,以及剩余部分 继续分解的结果。



动态规划有两种等价的实现方法:

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

public static int memoized_cut_rod(int[] p,int n)
{
	int result;
	int[] res=new int[n+1]; //res备忘数组
	for(int i=0;i<res.length;i++)
	{
		res[i]=-1;
	}
	result=cut_rod(p, n, res);
	return result;
}
public static int cut_rod(int[] p,int n,int[] res)
{
	int q=-1;
	if(n==0)
		q=0;
	if(res[n]>=0)
		return res[n];
	else 
	{
      for(int i=1;i<=n;i++)
      {
    	  q=Math.max(q, p[i]+cut_rod(p, n-i, res));
      }
	}
	res[n]=q;
	return q;
}

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

public static int bottom_up_cut_rod(int[] p,int n)
{
	int[] res=new int[n+1];
	res[0]=0;
	int q=-1;
	for(int i=1;i<=n;i++)
	{
		res[i]=-9999;
		for(int j=1;j<=i;j++)
		{
			q=Math.max(q, p[j]+res[i-j]);
		}
		res[i]=q;
	}
	return res[n];
}


两种方法得到的算法具有相同的渐进运行时间,仅有的差异是在某些特殊情况下,自顶向下的方法并未真正递归考察所有的的子问题,由于没有频繁的递归函数调用的开销,自底向上方法的时间复杂性函数通常具有更小的系数。


代码汇总:

package lianxi;

public class dp_cutrod {
public static int memoized_cut_rod(int[] p,int n)
{
	int result;
	int[] res=new int[n+1]; //res备忘数组
	for(int i=0;i<res.length;i++)
	{
		res[i]=-1;
	}
	result=cut_rod(p, n, res);
	return result;
}
public static int cut_rod(int[] p,int n,int[] res)
{
	int q=-1;
	if(n==0)
		q=0;
	if(res[n]>=0)
		return res[n];
	else 
	{
      for(int i=1;i<=n;i++)
      {
    	  q=Math.max(q, p[i]+cut_rod(p, n-i, res));
      }
	}
	res[n]=q;
	return q;
}
public static int bottom_up_cut_rod(int[] p,int n)
{
	int[] res=new int[n+1];
	res[0]=0;
	int q=-1;
	for(int i=1;i<=n;i++)
	{
		res[i]=-9999;
		for(int j=1;j<=i;j++)
		{
			q=Math.max(q, p[j]+res[i-j]);
		}
		res[i]=q;
	}
	return res[n];
}
public static void main(String[] args)
{
	int[] p={0,1,5,8,9,10,17,17,20,24,30};
	int n=9;
	int  result=memoized_cut_rod(p, n);//调用带备忘的自顶向下法
	int result2=bottom_up_cut_rod(p, n);//调用自底向上法
	System.out.print(result);
	System.out.print(result2);
}
}

输出结果为:25

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值