华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
一根X米长的树木,伐木工切割成不同长度的木材后进行交易,交易价格为每根木头长度的乘积。规定切割后的每根木头长度都为正整数;也可以不切割,直接拿整根树木进行交易。
请问伐木工如何尽量少的切割,才能使收益最大化?
二、输入描述
木材的长度(X ≤ 50)
三、输出描述
输出最优收益时的各个树木长度,以空格分隔,按升序排列
四、测试用例
测试用例1:
1、输入
10
2、输出
3 3 4
3、说明
- 一根2米长的树木,伐木工不切割,为2 * 1,收益最大为2。
- 一根4米长的树木,伐木工不需要切割为2 * 2,省去切割成本,直接整根树木交易,为4 * 1,收益最大为4。
- 一根5米长的树木,伐木工切割为2 * 3,收益最大为6。
- 一根10米长的树木,伐木工可以切割方式一:3, 4, 3, 也可以切割为方式二:3, 2, 2, 3,但方式二伐木工多切割一次,增加切割成本却买了一样的价格,因此并不是最优收益。
测试用例2:
1、输入
5
2、输出
2 3
3、说明
长度为5的木材,最优切割方案是将其切割成两段:长度为2和3。
方案1:5 = 2 + 3,收益为 2 × 3 = 6,切割次数为1。
其他切割方案如:5 = 1 + 4 或 5 = 5,都无法获得更高的收益。
因此输出结果为 2 3,代表最优收益的木材切割长度。
五、解题思路
1、动态规划
题目要求通过切割木材来最大化收益,这属于动态规划问题。
动态规划的状态定义为:dp[i] 记录长度为 i 的木材的最大收益及其切割方案。
通过遍历所有可能的切割点,并比较不同的切割方案来更新 dp 表,从而得到最终的最优解。
2、具体解题步骤:
- Wood 类定义了每段木材的最大收益 maxProfit 和对应的切割方案 cutLengths。
- 对于每一段长度 i 的木材,初始情况下不切割的最大收益是 i,并将它添加到 cutLengths 列表中。
- 外层循环 currentLength 遍历所有可能的木材长度,内层循环 cutPosition 通过尝试所有可能的切割位置计算木材的最大收益。如果新的切割方案的收益更高,或者收益相同但切割次数更少,更新动态规划数组 dp。
- 最后对得到的最优切割方案进行升序排列,并输出。
3、核心算法(动态规划思路):
- 对于长度为 i 的木材,遍历所有可能的切割点 j(从1到 i-1),考虑将木材切割成两段 j 和 i-j 的情况,计算切割后的总收益:
newProfit = dp[j].profit * dp[i - j].profit
- 如果
newProfit 比 dp[i].profit
更大,或者收益相同但切割次数更少(切割次数由 slices.size() 来衡量),则更新 dp[i]: - 更新
dp[i].profit 为 newProfit
。 - 更新
dp[i].slices 为 dp[j].slices 与 dp[i - j].slices
的合并。
六、Java算法源码
public class OdTest01 {
// 定义Wood类,用于记录最大收益和对应的切割方案
static class Wood {
int maxProfit; // 最大收益
ArrayList<Integer> cutLengths = new ArrayList<>(); // 对应的切割方案
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int woodLength = scanner.nextInt(); // 输入木材的总长度
Wood[] dp = new Wood[woodLength + 1]; // 动态规划数组,记录每个长度木材的最大收益和切割方案
// 初始化动态规划数组,每个长度的木材初始收益为不切割时的收益
for (int i = 0; i <= woodLength; i++) {
dp[i] = new Wood();
dp[i].maxProfit = i;
dp[i].cutLengths.add(i);
}
// 动态规划计算每个长度木材的最大收益及其切割方案
for (int currentLength = 2; currentLength <= woodLength; currentLength++) {
for (int cutPosition = 1; cutPosition < currentLength; cutPosition++) {
int newProfit = dp[cutPosition].maxProfit * dp[currentLength - cutPosition].maxProfit;
// 如果当前切割方案的收益更大,或者收益相同但切割次数更少,则更新切割方案
if (newProfit > dp[currentLength].maxProfit
|| (newProfit == dp[currentLength].maxProfit
&& dp[currentLength].cutLengths.size() > dp[cutPosition].cutLengths.size() + dp[currentLength - cutPosition].cutLengths.size())) {
dp[currentLength].maxProfit = newProfit;
dp[currentLength].cutLengths.clear();
dp[currentLength].cutLengths.addAll(dp[cutPosition].cutLengths);
dp[currentLength].cutLengths.addAll(dp[currentLength - cutPosition].cutLengths);
}
}
}
// 对最终的切割方案进行升序排序
dp[woodLength].cutLengths.sort(Integer::compareTo);
// 打印最终的切割方案
StringJoiner result = new StringJoiner(" ");
for (int length : dp[woodLength].cutLengths) {
result.add(Integer.toString(length));
}
System.out.println(result);
}
}
七、效果展示
1、输入
17
2、输出
2 3 3 3 3 3
3、说明
对于长度为17的木材,最优的切割方案是将其切割成六段:一段长度为2,其余五段长度为3。
方案:17 = 2 + 3 + 3 + 3 + 3 + 3,收益为 2 × 3 × 3 × 3 × 3 × 3 = 486,切割次数为5。
为什么选择这种切割方式:
- 收益最大化:切割成长度为2和3的组合时,乘积收益达到最大值。这个组合虽然在长度上比较均衡,但相乘的结果是最大的。
- 切割次数较少:通过5次切割就可以将木材分割成6段,得到了最大的收益值。
其他可能的切割方案,例如长度为1的切割,或者长度更大的组合(如4或5),都不会提供比上面组合更大的收益。
因此,最终输出为 2 3 3 3 3 3,代表最大收益的木材切割长度。
🏆下一篇:华为OD机试 - 简易内存池 - 逻辑分析(Java 2024 E卷 200分)
🏆本文收录于,华为OD机试(JAVA)真题(E卷+D卷+A卷+B卷+C卷)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。