题解:洛谷 P5019 铺设道路
【题目来源】
洛谷:P5019 [NOIP 2018 提高组] 铺设道路 - 洛谷(luogu.com.cn)
【题目描述】
春春是一名道路工程师,负责铺设一条长度为 \(n\) 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 \(n\) 块首尾相连的区域,一开始,第 \(i\) 块区域下陷的深度为 \(d_i\)。
春春每天可以选择一段连续区间 \([L,R]\) ,填充这段区间中的每块区域,让其下陷深度减少 \(1\)。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 \(0\)。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 \(0\)。
【输入】
输入文件包含两行,第一行包含一个整数 \(n\),表示道路的长度。第二行包含 \(n\) 个整数,相邻两数间用一个空格隔开,第 \(i\) 个整数为 \(d_i\)。
【输出】
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
【输入样例】
6
4 3 2 5 3 5
【输出样例】
9
【核心思想】
-
问题分析:给定长度为 \(n\) 的道路,每块区域下陷深度为 \(d_i\)。每天可选择一段连续非零下陷区间,将区间内每块深度减 \(1\)。求最少天数使所有深度变为 \(0\)。这是一个贪心差分问题,与积木大赛(P1969)本质相同,关键在于识别"上升段"需要额外操作。
-
算法选择:
- 贪心累加差分:从左到右遍历,若当前深度 \(d_i > d_{i-1}\),则需要额外 \(d_i - d_{i-1}\) 天操作来填补上升缺口
-
关键步骤:
- 读入数据:\(n\) 和深度数组 \(d[1..n]\)(\(d[0] = 0\))
- 贪心计算(\(i\) 从 \(1\) 到 \(n\)):
- 若 \(d[i] > d[i-1]\):
ans += d[i] - d[i-1]
- 若 \(d[i] > d[i-1]\):
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),线性遍历
- 空间复杂度:\(O(n)\),深度数组(可优化为 \(O(1)\))
-
贪心差分的核心思想:
- 区间覆盖的叠加性:每次操作给连续区间 \(-1\),多个操作叠加使每个位置达到目标深度 \(0\)
- 上升缺口的必要性:若 \(d_i > d_{i-1}\),位置 \(i\) 比位置 \(i-1\) 深的部分无法被包含 \(i-1\) 的区间操作覆盖(因为那些操作也会给 \(i-1\) 减 \(1\),而 \(i-1\) 可能已经为 \(0\) 了),必须单独开启新的区间操作
- 下降段的复用:若 \(d_i \leq d_{i-1}\),位置 \(i\) 的深度可以被覆盖 \(i-1\) 的区间操作顺带覆盖,无需额外天数
- 与积木大赛的等价性:积木大赛是区间 \(+1\) 达到目标高度,本题是区间 \(-1\) 达到目标深度 \(0\),数学结构完全相同
- 适用于"区间增量/减量覆盖序列目标"的贪心问题,核心在于差分思想识别必须单独开启的操作
【解题思路】

【算法标签】
普及 #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[100005], f[100005]; // n: 数组长度, a: 输入数组, f: 动态规划数组
int main()
{
// 输入数组长度n
cin >> n;
// 输入数组元素
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
// 初始化动态规划数组
f[1] = a[1]; // 第一个元素的最小和就是它本身
// 动态规划计算最小和
for (int i = 2; i <= n; i++) {
if (a[i - 1] >= a[i]) {
// 如果前一个元素大于等于当前元素,直接继承前一个结果
f[i] = f[i - 1];
} else {
// 否则需要增加差值部分
f[i] = f[i - 1] + (a[i] - a[i - 1]);
}
}
// 输出最终结果
cout << f[n];
return 0;
}
【运行结果】
6
4 3 2 5 3 5
9
浙公网安备 33010602011771号