7-9 切原木问题 java 题解

给定一根长度为N米的原木;另有一个分段价格表,给出长度L1,L2,...Li,...Lk米所对应的价格P1,P2...Pk(Li,Pi均为正整数),求切割原木分段出售所能获得的最大收益。 例如,根据下面给出的价格表,

Li12345678910
Pi1589101717202328

若要出售一段8米长的原木,最优解是将其切割为2米和6米的两段,这样可以获得最大收益=L2+L6=5+17=22。而若要出售一段3米长的原木,最优解是根本不要切割,直接售出。

输入格式:

首行输入N,k,紧接着第二行为k个Li(递增有序)和第三行对应的k个Pi值。 (0<N,k<1000) 。

输出格式:

对应原木的最大切割收益(题目中保证最大收益值在int范围)。

输入样例:

在这里给出一组输入。例如:

8 10
1 2 3 4 5  6  7  8  9  10
1 5 8 9 10 17 17 20 23 28

输出样例:

在这里给出相应的输出。例如:

22

解题思路:

经典动态规划问题,要想得到最佳解,在每求一步时都必须充分利用之前的局部解,其中最关键的是要列出动态转移方程。这道题可以假设从第一米到第n米,每一米先只用一种切割方式时,求出各米的最佳收益,存放至一维dp数组中。然后再加一种切割方式,此时在切割时,有种错误的思路是全用新的切割方式,切的剩下的在dp数组中取值,这种思路一定是错误的,因为可能在切割时,只是新切割方式用一次或两次收益即可达到最大值。正确的切割方式为:仍从第一米到第n米,在每次切割的基础上都只用一次新切割方式,切掉新方式时剩余的dp值加上此次切割的收益,两者相加与原始dp相比取最大值。即得到转移方程:dp[j] = max(dp[j - t[i]] + p[i] , dp[j])。

 提给出得样例的dp二维数组为:

在列二维数组时要注意将第零行和第零列空出来,每一横行表示用一种切割方式切不同长度的木材价值,每一纵列表示某米长的木材用不同的方式切割可以取到的价值。

核心java代码为:(二维dp)

for(int i = 0; i < m;i++) {
	for(int j = 1;j <= n;j++) {
		if(j < t[i]) {//填充不足t[i]米时的情况
			dp[i + 1][j] = dp[i][j];
		}else {
			dp[i + 1][j] = Math.max(dp[i][j], dp[i + 1][j - t[i]] + p[i]);
		}
	}
}

 二维数组会有较大空间浪费,可优化为一维数组:dp[n + 1] 只保存每米切割可以取到的最大值,对其余值不保存。

java代码:(一维dp)

import java.util.Scanner;

public class Main {
	public static void main(String[] args){
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();//木头长度
		int m = scan.nextInt();//题给出的数组长度
		int []t = new int[m];
		int []p = new int[m];
		int []dp = new int[n + 1];
		for(int i = 0; i < m;i++) {
			t[i] = scan.nextInt();
		}
		for(int i = 0; i < m;i++) {
			p[i] = scan.nextInt();
		}
		scan.close();
		
		for(int i = 0; i < m;i++) {
			for(int j = t[i];j <= n;j++) {
				dp[j] = Math.max(dp[j], dp[j - t[i]] + p[i]);
			}
		}
		System.out.println(dp[n]);
	}
}

PTA提交截图:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值