动态规划_钢条切割问题

       问题来自<算法导论>一书,大致意思是,有一段长度为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

      谢谢!!


 

 

posted on 2013-11-14 20:38  you Richer  阅读(445)  评论(0编辑  收藏  举报