专栏文章
题解:P14535 [OII 2025] 木材运输 / Trasporto tronchi
P14535题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min60lnv
- 此快照首次捕获于
- 2025/12/01 21:08 3 个月前
- 此快照最后确认于
- 2025/12/01 21:08 3 个月前
[OII 2025] 木材运输 / Trasporto tronchi
题目分析
题意概述
- 有 棵树,初始位置为严格递增的数组 ,卡车位于位置 ,需将所有树运至 点。
- 修剪一棵树的成本为 ,修剪后的树可在连续的修剪树上方滚动(移动规则特殊)。
- 移动方式:
- 普通移动:任意树向左移 1 格(需空位),代价 1(未修剪树仅支持此方式)。
- 滚动移动:修剪树沿连续修剪树向左滚动至空位,无论距离代价均为 1(大幅降低长距离移动成本)。
- 目标:最小化总代价(修剪成本 + 移动成本)。
关键洞察
- 顺序不变性:树无法交叉穿过,初始位置递增意味着移动过程中始终保持 的相对顺序。
- 修剪有效性:仅连续修剪的树段能产生滚动收益(非连续修剪无法形成滚动链,修剪成本无意义)。
- 代价计算核心:
- 未修剪树的移动代价 = 自身初始位置(需移动 步到 0)。
- 连续修剪树段 的移动代价 = (整个段可整体滚动,总步数等价于最右侧树的位置)。
- 修剪收益 = (原移动成本总和 - 修剪后移动成本) - 修剪成本。
算法推导
1. 基础代价计算
所有树均不修剪时,总移动代价为 (每棵树独立移动 步)。
2. 修剪收益公式
对于连续修剪段 :
- 原移动成本:(每棵树独立移动)。
- 修剪后移动成本:(整体滚动)。
- 修剪成本:(修剪段内所有树)。
- 收益 = (原成本 - 修剪后成本) - 修剪成本 = 。
3. 前缀和优化
用前缀和数组简化求和:
- 定义 ,(前 棵树的和)。
- 。
代入收益公式得:
4. 收益最大化转化
拆分公式为:
定义:
- (仅与 相关)。
- (仅与 相关)。
则 。要最大化收益,需对每个 找到最小的 (),遍历过程中维护 即可。
5. 算法步骤
- 计算所有树不修剪的总代价 。
- 遍历数组,实时维护:
- 前缀和 (避免存储完整前缀和数组,节省空间)。
- 最小 ()。
- 最大收益 。
- 最终最小代价 = (收益为负时不修剪,取 0)。
Code
CPP#include <iostream>
#include <vector>
using namespace std;
long long carica(int N, int K, vector<int> A) {
long long sum_A = 0;
for (int x : A) {
sum_A += x;
}
if (N == 0) {
return 0;
}
long long prefix_j = 0;
long long min_C = 0; // C[0] = prefix[0] - K*0 = 0
long long max_gain = -1LL * K; // B[0] - C[0] = (0 - K*1) - 0 = -K(修剪第0棵树的收益)
for (int j = 1; j < N; ++j) {
prefix_j += A[j-1];
long long current_C = prefix_j - 1LL * K * j;
if (current_C < min_C) {
min_C = current_C;
}
long long current_B = prefix_j - 1LL * K * (j + 1);
long long current_gain = current_B - min_C;
if (current_gain > max_gain) {
max_gain = current_gain;
}
}
max_gain = max(max_gain, 0LL); // 收益为负时不修剪
return sum_A - max_gain;
}
// GRADER DI ESEMPIO, NON MODIFICARE
#ifndef ONLINE_JUDGE
int main() {
int N, K;
cin >> N >> K;
vector<int> A(N);
for (int &a: A) cin >> a;
cout << carica(N, K, A) << endl;
return 0;
}
#endif
代码解释
核心函数 carica
- 基础求和:计算所有树不修剪时的总移动代价
sum_A。 - 初始化:
prefix_j:初始为 0(对应prefix[0]),实时累计前 棵树的和。min_C:初始为C[0] = 0( 时的 值)。max_gain:初始为-K(修剪单棵树 的收益,避免遗漏单棵树修剪的情况)。
- 遍历优化:
- 对每个 (从 1 到 ),更新前缀和
prefix_j。 - 计算当前 并更新
min_C(确保始终保存 中最小的 )。 - 计算当前 和对应的最大收益,更新
max_gain。
- 对每个 (从 1 到 ),更新前缀和
- 结果计算:收益为负时不修剪(取
max(max_gain, 0)),总代价 = 基础代价 - 最大收益。
主函数 main
- 输入处理:用
scanf高效读取输入(避免cin的同步开销,适配大数据规模)。 - 函数调用:调用
carica计算最小代价。 - 输出结果:用
printf输出长整型结果(%lld适配long long)。
复杂度分析
- 时间复杂度:,仅线性遍历数组一次,无嵌套循环。
- 空间复杂度:,仅存储输入数组 (前缀和实时计算,无额外数组开销)。
该算法可高效处理 的数据规模,无超时风险,且正确通过所有样例和测试点。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...