动态规划之钢条切割问题

来源:互联网 发布:网络剧备案查询 编辑:程序博客网 时间:2024/06/06 08:24

钢条切割问题:

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

长度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