动态规划问题探究及其Java实现
来源:互联网 发布:js对象深拷贝 编辑:程序博客网 时间:2024/06/06 16:55
问题引入:
某公司出售的长为i的钢条的价格为p(i)(单位为元),如下表所示。如果有一段长为n的钢条,求如何切割才能使收益最大?
测试的最优方案:
1.最容易想到的解法
思路:我们把长度为n的钢条分为i和n-i两部分,1<=i<=10,且不需要再分割,则只需要对n-i求最大收益相加即可。同理对n-i也分为两段,左边的不需要分解,继续分解右边的,直到右边的部分为0。这是一种自顶向下的递归方法。具体代码如下:
public class Guihua {public int cutProfit(int len) { int sum=0; int[] price= {0,1,5,8,9,10,17,17,20,24,30};//长度价格表 if(len==0) return 0; for(int i=1;i<=len && i<price.length;i++) {int tmpsum=price[i]+cutProfit(len-i);//将len长度的钢条切割成两段,递归求解第二段if(tmpsum>sum)sum=tmpsum; } return sum; }public static void main(String[] args){ long startTime=System.currentTimeMillis(); System.out.println(new Guihua().cutProfit(20)); long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: "+(endTime-startTime)+"ms");}}
运行上述代码,结果与上标一致。但是运行时间很慢,n=20时的运行结果长达363ms。n=50,超时,没有运行出结果。代码之所以效率很低,是应为上述递归方法反复求解相同的子问题。如n=5,需要求n=3,;n=4,又需要求n=3。这时就需要用动态规划算法了。
2.动态规划算法
对上述问题,运用动态对话算法仔细安排求解顺序,对每个子问题只需要求解一次,并将结果保存下来,下次用到该结果时,只需要直接读取,不需要重复计算。因此动态规划方法时付出额外的内存空间来节省计算时间,时时空权衡的例子。
动态规划有两种等价的实现方法,分别是带备忘录的自顶向下法和自底向上法,以钢条切割问题展示如下:
2.1带备忘录的自顶向下法
第一种为带备忘录的自顶向下法。此方法仍用递归实现,但是过程会保存子问题 的解(可以保存在数组中)。需要子问题的解时,先检查是否保存该值,如保存直接用;否则,按上面的方法计算。这其实是一种不断分解的方法。在Java实现中,meArray存储计算好的最优子问题的解,由于在递归程序中初始化数组会破坏已经赋值的数组元素,所以将该数组放置函数外面初始化,并用函数调用。
带备忘录的自顶向下法实现:
public class Guihua { int fromTop(int len,int[] meArray) { int sum=0; int[] price= {0,1,5,8,9,10,17,17,20,24,30};//长度价格表 if(len==0) return 0; if(meArray[len]!=0) //已存,直接使用 return meArray[len]; for(int i=1;i<=len && i<price.length;i++)//前半段只能是1到10,没有大于10的规格的钢条 {int tmpsum=price[i]+fromTop(len-i,meArray);if(tmpsum>sum)sum=tmpsum; } meArray[len]=sum;return sum; } public static void main(String[] args){ long startTime=System.currentTimeMillis(); int len=20; int[] meArray=new int[len+1];//记忆数组,记录已经算出的值 System.out.println( new Main().fromTop(len, meArray)); for(int ele:meArray) System.out.print(ele+" "); long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: "+(endTime-startTime)+"ms");}}查看运行结果发现n=20,用时3ms,输出的记忆数组为:0 1 5 8 10 13 17 18 22 25 30 31 35 38 40 43 47 48 52 55 60,程序效率提高。但是程序中没有给出最优解得具体形式。下面给出方法:
2.1.1含有最优方案的带备忘录的自顶向下法
由上一阶段,我们得到了对不同n对应的最大收益。第一段长度先固定,求第二段的分解方案,如果我们比较出一个较大的第二段长度,就把对应的长度存起来,这样最后存的肯定是收益最大时的其中一段长度,我们把它称作确定段长度。由总长度和确定段长度,二者相减就可以确定下一段长度;然后由下一段长度和它对应的确定长度又可确定下下段长度,然后依次运算,直到相减等于0。可以参考下面的表格:
具体实现仍采用递归的方法:
public class Guihua { int fromTopDetail(int len, int[] meArray, int[] detailArray) { int sum=0; int[] price= {0,1,5,8,9,10,17,17,20,24,30};//长度价格表 if(len==0) return 0; if(meArray[len]!=0) // return meArray[len]; for(int i=1;i<=len && i<price.length;i++) {int tmpsum=price[i]+fromTopDetail(len-i,meArray,detailArray);if(tmpsum>sum){sum=tmpsum;detailArray[len]=i;} } meArray[len]=sum;return sum;} void getDeatil(int[] methodArray,int index) { //由第一段长度,求解优方案if(methodArray[index]==index){System.out.println(methodArray[index]);return;}elseSystem.out.println(methodArray[index]);getDeatil(methodArray,index-methodArray[index]);} public static void main(String[] args){ long startTime=System.currentTimeMillis(); int len=9; int[] meArray=new int[len+1];//记忆数组,记录已经算出的值 int[] detailArray=new int[len+1];//记忆数组,记录已经算出的值 System.out.println( new Main().fromTopDetail(len, meArray, detailArray)); new Main().getDeatil(detailArray,detailArray.length-1); long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: "+(endTime-startTime)+"ms");}}
2.2含具体方案的自底向上法
第二种方法称为自底向上法。这种方法的思路是,任何问题都依赖比它更小的问题的求解。依次将问题按照从小到大的问题求解,当求到某个问题时,它所依赖的更小的问题都已求解完毕,结果已保存。每个子问题只需要求解一次。这其实是一种自小而大组合的方法。具体的实现不采用递归的方式,而是采用循环,用小的子问题的结果依次组合到大的问题,函数返回一个具体方案数组detailArray,就是对每个小于等于n的钢条,存储它的确定长度。由detailArray求解具体方案,和上面方法一样。
具体实现:
public class Guihua { private int[] fromBottomDetail(int len) { int[] price= {0,1,5,8,9,10,17,17,20,24,30};//长度价格表 int[] meArray=new int[len+1]; int[] detailArray=new int[len+1]; int sum=0; for(int i=0;i<=len;i++) { for(int j=0;j<=i && j<price.length;j++) { if(sum<price[j]+meArray[i-j]) {sum=price[j]+meArray[i-j]; meArray[i]=price[j]+meArray[i-j]; detailArray[i]=j; } } } System.out.println(sum); return detailArray;} void getDeatil(int[] methodArray,int index) { //由第一段长度,求解优方案if(methodArray[index]==index){System.out.println(methodArray[index]);return;}elseSystem.out.println(methodArray[index]);getDeatil(methodArray,index-methodArray[index]);} public static void main(String[] args){ long startTime=System.currentTimeMillis(); int len=7; int[] meArray=new int[len+1];//记忆数组,记录已经算出的值 int[] detailArray=new Guihua().fromBottomDetail(len); new Main().getDeatil(detailArray,detailArray.length-1); long endTime=System.currentTimeMillis(); //获取结束时间 System.out.println("程序运行时间: "+(endTime-startTime)+"ms");}}
阅读全文
0 0
- 动态规划问题探究及其Java实现
- 背包问题动态规划详细探究
- 动态规划-背包问题九讲及其代码实现
- 动态规划原理及其实现
- 动态规划经典问题Java实现
- 动态规划求解硬币找零问题——Java实现
- 动态规划算法0-1背包问题java实现
- 算法java实现--动态规划--电路布线问题
- 算法java实现--动态规划--流水作业调度问题
- 算法java实现--动态规划--0-1背包问题
- 动态规划求解矩阵连乘问题Java实现
- 《JAVA》中利用《动态规划》实现《背包》问题
- 背包问题动态规划算法java实现及分析
- 动态规划求解背包问题(JAVA实现)
- 动态规划之背包问题01--java实现
- 动态规划解决01背包问题(java实现)
- 动态规划之背包问题——Java实现
- 动态规划之背包问题01——Java实现
- 关于sklearn.svm.SVC与.NuSVC的区别以及参数介绍
- 读《Head First设计模式》
- Data Binding Library(Data Binding库官方指南)
- SSH(Struts,Spring,Hibernate)和SSM(SpringMVC,Spring,MyBatis)的区别
- viewpage轮播
- 动态规划问题探究及其Java实现
- 摄像机坐标系
- dubbo——广播方式和zoomkeeper方式
- C语言的整型溢出问题 int、long、long long取值范围 最大最小值
- boost 之property_tree操作xml文件
- 用MMDrawerController实现侧滑
- 日历考勤记录
- SpirngMVC异常处理
- android tablelayout嵌套tablayout Fragment嵌套Fragment