伐木工 - 华为OD统一考试

OD统一考试

题解: Java / Python / C++

题目描述

一根X米长的树木,伐木工切割成不同长度的木材后进行交易,交易价格为每根木头长度的乘积。规定切割后的每根木头长度都为正整数,也可以不切割,直接拿整根树木进行交易。请问伐木工如何尽量少的切割,才能使收益最大化?

输入描述

木材的长度(X<=50)

输出描述

输出最优收益时的各个树木长度,以空格分割,按升序排列

示例1

复制代码
输入:
10

输出:
3 3 4

说明:
1.一根2米长的树木,伐木工不切割,为2*1,收益最大为2
2.一根4米长的树木,伐木工不需要切割为2*2,省去切割成本,直接整根树木交易,为4*1,收益最大为4
3.一根5米长的树木,伐木工切割为2*3,收益最大为 6
4.一根10米长的树木,伐木工可以切割为方式: 3,4,3,也可以切割为方式二:3,2,2,3,但方式二代木工多切割了一次增加切割成本却卖了一样的价格,因此并不是最优收益。

题解

动态规划类型的问题。

通过动态规划的方法:

1、定义一个状态数组 dp,其中 dp[x] 表示长度为 x 的树木的最大化收益。

2、定义一个数组 d ,其中 d[x] 用于记录长度为 x 的树木达到最大收益时,最后一节的长度(收益相同取切割次数最少的)。

3、定义 times , 其中 times[x] 数组表示长度为x的树木最小需要切割的次数。

动态规划的状态转移方程为:dp[x]=max⁡(dp[x],dp[j]×(x−j)), for j∈[1,x−1]

这表示尝试对长度为 x 的树木进行切割,寻找使收益最大的切割方式。

最后,我们通过回溯 d 数组,获取切割的具体方式,并按升序排列输出。

代码中,使用了两个数组 dpd 分别表示最大收益和最后一节的长度,遍历计算得到最优解。时间复杂度为 O(X^2),其中 X 为树木的长度。空间复杂度为 O(X)。

Java

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/**
 * @author code5bug
 */
public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int x = scanner.nextInt();

        // dp[x] 表示长度为 x 的树木最大化的收益
        int[] dp = new int[x + 1];
        // times[x]使长度为 x 的树木最大化的收益,最小需要切割的次数
        int[] times = new int[x + 1];
        // d[x] 表示长度为 x 的树木要达到最大收益最后一节的长度
        int[] d = new int[x + 1];

        for (int i = 1; i <= x; i++) {
            // 不切割时的收益
            dp[i] = d[i] = i;

            // 尝试对长度为 i 的树木进行切割以获取最大收益
            for (int j = 1; j < i; j++) {
                if (dp[i - j] * j > dp[i]) { //  切割出长度 j 的一段,判断是否能收益变大
                    d[i] = j;
                    dp[i] = dp[i - j] * j;
                    times[i] = times[i - j] + 1;
                } else if (dp[i - j] * j == dp[i] && times[i] > times[i - j] + 1) { // 收益相同但切割次数少
                    d[i] = j;
                    times[i] = times[i - j] + 1;
                }
            }
        }

        int idx = x;
        ArrayList<Integer> rs = new ArrayList<>();
        while (idx > 0) {
            rs.add(d[idx]);
            idx -= d[idx];
        }

        Collections.sort(rs);
        for (int i : rs) {
            System.out.print(i + " ");
        }
    }
}

Python

python 复制代码
x = int(input())

# dp[x] 表示长度为 x 的树木最大化的收益
dp = [i for i in range(x + 1)]
# 使长度为 x 的树木最大化的收益,最小需要切割的次数
times = [0] * (x + 1)
# d[x] 表示长度为 x 的树木要达到最大收益最后一节的长度
d = [i for i in range(x + 1)]

for i in range(1, x + 1):
    for j in range(1, i):  # 尝试对长度为 i 的树木进行切割以获取最大收益
        if dp[i - j] * j > dp[i]:  # 切割出长度 j 的一段,判断是否能收益变大
            d[i] = j
            dp[i] = dp[i-j] * j
            times[i] = times[i-j] + 1
        elif dp[i-j] * j == dp[i] and times[i] > times[i-j] + 1:  # 收益相同但切割次数少
            d[i] = j
            dp[i] = dp[i-j] * j
            times[i] = times[i - j] + 1

idx, rs = x, []
while idx > 0:
    rs.append(d[idx])
    idx -= d[idx]

rs.sort()
print(*rs)

C++

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int x;
    cin >> x;

    // dp[x] 表示长度为 x 的树木最大化的收益
    vector<int> dp(x + 1);
    // times[x] 使长度为 x 的树木最大化的收益,最小需要切割的次数
    vector<int> times(x + 1);
    // d[x] 表示长度为 x 的树木要达到最大收益最后一节的长度
    vector<int> d(x + 1);

    for (int i = 1; i <= x; i++) {
        // 不切割时的收益
        dp[i] = d[i] = i;

        // 尝试对长度为 i 的树木进行切割以获取最大收益
        for (int j = 1; j < i; j++) {
            if (dp[i - j] * j > dp[i]) { // 切割出长度 j 的一段,判断是否能收益变大
                d[i] = j;
                dp[i] = dp[i - j] * j;
                times[i] = times[i - j] + 1;
            } else if (dp[i - j] * j == dp[i] && times[i] > times[i - j] + 1) { // 收益相同但切割次数少
                d[i] = j;
                times[i] = times[i - j] + 1;
            }
        }
    }

    int idx = x;
    vector<int> rs;
    while (idx > 0) {
        rs.push_back(d[idx]);
        idx -= d[idx];
    }

    sort(rs.begin(), rs.end());

    for (int i : rs) {
        cout << i << " ";
    }

    return 0;
}

🙏整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

相关推荐
xhbaitxl2 分钟前
算法学习day39-动态规划
学习·算法·动态规划
I_LPL4 分钟前
day23 代码随想录算法训练营 回溯专题2
算法·hot100·回溯算法·求职面试
智者知已应修善业5 分钟前
【洛谷P9975奶牛被病毒传染最少数量推导,导出多样例】2025-2-26
c语言·c++·经验分享·笔记·算法·推荐算法
猿小羽5 分钟前
AIGC 应用工程师(3-5 年)面试题精讲:从基础到实战的系统备战清单
面试·大模型·aigc·agent·rag
玩大数据的龙威9 分钟前
农经权二轮延包—各种地块示意图
python·arcgis
Trouvaille ~10 分钟前
【Linux】应用层协议设计实战(一):自定义协议与网络计算器
linux·运维·服务器·网络·c++·http·应用层协议
ZH154558913111 分钟前
Flutter for OpenHarmony Python学习助手实战:数据库操作与管理的实现
python·学习·flutter
CSCN新手听安16 分钟前
【linux】高级IO,I/O多路转接之poll,接口和原理讲解,poll版本的TCP服务器
linux·运维·服务器·c++·计算机网络·高级io·poll
belldeep20 分钟前
python:用 Flask 3 , mistune 2 和 mermaid.min.js 10.9 来实现 Markdown 中 mermaid 图表的渲染
javascript·python·flask
喵手20 分钟前
Python爬虫实战:电商价格监控系统 - 从定时任务到历史趋势分析的完整实战(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·电商价格监控系统·从定时任务到历史趋势分析·采集结果sqlite存储