算法导论中的钢条切割问题

问题描述:
  公司购买长钢条,将其切割为短钢条进行出售,切割工序本身没有成本支出,公式管理层希望知道最佳的切割方案。假定我们知道公司出售一段长为i英寸的钢条价格为pi;钢条的长度均为整英寸。给定一段长度为n英寸的钢条和一个价格表pi,求切割钢条方案,使得销售收益最大。如果长度为n英寸的钢条的价格足够大,最优解可能就是完全不用切割。
长度:1, 2, 3, 4, 5, 6, 7, 8, 9,10
对应价格:1,5, 8,16,10, 17, 17, 20, 24, 30。

解法一:
  用记忆型递归进行求解:比如一段长度为10的钢条,保留1英寸,将剩下的9英寸进行切割;也可以保留2英寸,将剩下的8英寸进行切割,以此类推。所以递归树的根节点有十个分支。剩下的节点要切割几英寸就有几个分支。由于在进行递归的时候有很多重复的子问题,导致相同的问题进行多次求解;所以为了提高效率可以将递归过程中得到的解进行记录。这称为记忆型递归或者记录型递归。

代码如下:

import java.util.*;

public class 钢条切割 {
	static int[] p = {0, 1, 5, 8, 16, 10, 17, 17, 20, 24, 30};//价格表,第零个位置不用
	static int[] vs = new int[10+1];//记录表
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		Arrays.fill(vs, -1);//初始化记录表
		System.out.println(dfs(10));
	}
	private static int dfs(int n) {
		if(n==0){
			return 0;
		}
		if(vs[n]>=0){//检查是否有记录
			return vs[n];
		}else{
			int v = 0;
			for(int i=1; i<=n; i++){//保留i英寸
				v = Math.max(v, p[i] + dfs(n-i));//选出分支中的最大值
			}
			vs[n] = v;//进行记录
			return v;
		}
	}
}


解法二:
  用动态规划进行求解;和记忆型递归有些类似。只是建立了一张完整的后返回结果。

代码如下:

import java.util.*;

public class 钢条切割 {
	static int[] p = {0, 1, 5, 8, 16, 10, 17, 17, 20, 24, 30};//价格表,第零个位置不用
	static int[] dp = new int[10+1];//dp表,下标表示长度,值表示对应长度的最大价值
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		Arrays.fill(dp, 0);//初始化记录表
		dp[1] = p[1];//长度为1时无法分割,所以直接可以得出最大价值
		System.out.println(dpf(10));
	}
	/*
	 * 动态规划,建立dp表
	 */
	private static int dpf(int n) {
		for(int i=1; i<=n; i++){//求dp[i]
			int v = 0;
			for(int j=1; j<=i; j++){
				v = Math.max(v, p[j]+dp[i-j]);
			}
			dp[i] = v;
		}
		return dp[n];
	}
	
}


总结:
  对于这个钢条切割问题最主要是能想到保留一定长度,切割剩下的长度,然后不断递归;以及能考虑到递归过程中会出现多次子问题重复求解。动态规划最重要的是如何用前面的记录推导后面的记录;也就是找出dp公式。

猜你喜欢

转载自blog.csdn.net/HC199854/article/details/104874096