动态规划_钢条切割问题
问题来自<算法导论>一书,大致意思是,有一段长度为n的钢条,钢条可以被分割成不同的长度的小钢条出售,不同的小钢条对应不同的售价。如长度为1售价为1,长度为2售价为3。详见下表:
问题分解:首先这个问题的解共有2的n-1次方,因为将n长的钢条分成1,2,3,4,...,n-1个节点,每个节点都有切于不切的两种选择。其次,每中切割方案都可以分成一段长度为i(0<=i<=n)的不再分割钢条和一段n-i的再分割钢条。其中的n-i就是一个子问题,最优解就是这两者的价值和最大的情况。即:max(Vn,Vn-1+R1,Vn-2+R2,...,V1+Rn-1,V0+Rn),其中V0 = 0,R0 = 0。即有状态转移公式:
其中Rn表示n长的钢条最大的价值,Vn表示不切割的长度的钢条的售价。
继续扯动态规划的思想,首先动态规划的关键在于重复子问题特性。因为这一特性导致了动态规划思想优于分治法,动态规划将重复子问题的解保存起来,再自底向上求解上层子问题(也可以称为子阶段,如背包问题中,我认为称为子阶段更恰当)。动态规划很仔细的安排子问题的求解顺序,即在求解一个某个子问题时,该子问题的子子问题的解都已经求的,并被保存起来了,只需从内存中读取,而无需再次求解。这就话很重要啊,这就是为什么在解目标长度为n的钢条切割问题时,要使n从1开始递增的原因了。好了,关于动态规划的思想,留些下次再扯吧,下面是程序代码。
package com.wly.algorithmbase.dailyproblem; /** * 动态规划解钢条切割问题 * 动态规划的关键在于利用重复子问题 * @author wly * */ public class DivideNSteel { //对应不同长度的钢条价值,如1-1,2-2,3-5,4-7,5-9.... static int[] dividePriceForm = {0,1,3,12,20,22,23,24,25,28,33}; public static void main(String[] args) { System.out.println(solve(dividePriceForm.length)); } /** * 状态转移方程 Rn = max(Vn,V1 + Rn-1,V2 + Rn-1,V3 + Rn-3,,,,Vn-1 + R1),其中Vn表示n长的钢条不切割的价值,Rn表示长度为n的子问题的最优解 * @param n * @return */ public static int solve(int n) { int[] result = new int[n]; //用于存储各个子问题的最优解,当然这里面也包含了全局最优解result[n] //将所有子问题结果都先赋值成"未知值",在自底向上求解问题的过程中,用于比较最优子问题解 for(int i=0;i<result.length;i++) { result[i] = 0; } result[1] = dividePriceForm[1]; if(n == 0) { //如果长度为0,则直接返回 return 0; } else { //这里仔细安排的子问题的求解顺序,使在求解某个子问题时,其子子问题已经有解了 for(int i=1;i<n;i++) { //求解各个最优子问题表示不切割的钢条长度 for(int j=1;j<=i;j++) { result[i] = Math.max(result[i], result[i-j] + dividePriceForm[j]); } } } for(int i:result) { System.out.print(i + " "); } System.out.println(); return result[n-1]; } }
运行结果:
0 1 3 12 20 22 24 32 40 42 44 44
O啦~~~
转帖请保留出处:http://blog.csdn.net/u011638883/article/details/16120505
谢谢!!