题解:洛谷 P1969 积木大赛
【题目来源】
洛谷:P1969 [NOIP 2013 提高组] 积木大赛 - 洛谷
【题目描述】
春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为 \(n\) 的大厦,大厦可以看成由 \(n\) 块宽度为 \(1\) 的积木组成,第 \(i\) 块积木的最终高度需要是 \(h_i\)。
在搭建开始之前,没有任何积木(可以看成 \(n\) 块高度为 \(0\) 的积木)。接下来每次操作,小朋友们可以选择一段连续区间 \([l, r]\),然后将第 \(L\) 块到第 \(R\) 块之间(含第 \(L\) 块和第 \(R\) 块)所有积木的高度分别增加 \(1\)。
小 M 是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。
【输入】
包含两行,第一行包含一个整数 \(n\),表示大厦的宽度。
第二行包含 \(n\) 个整数,第 \(i\) 个整数为 \(h_i\)。
【输出】
建造所需的最少操作数。
【输入样例】
5
2 3 4 1 2
【输出样例】
5
【核心思想】
-
问题分析:给定目标高度序列 \(h_1, h_2, \ldots, h_n\),初始全为 \(0\)。每次操作可选择一段连续区间 \([l, r]\),将区间内所有高度 \(+1\)。求最少操作次数使序列达到目标。这是一个贪心差分问题,关键在于发现:每次操作对应给某个区间 \(+1\),最少操作次数等于所有"上升段"的高度差之和。
-
算法选择:
- 贪心累加差分:从左到右遍历,若当前高度 \(h_i > h_{i-1}\),则需要额外 \(h_i - h_{i-1}\) 次操作来填补这个"上升缺口"
-
关键步骤:
- 读入数据:\(n\) 和高度数组 \(h[1..n]\)(\(h[0] = 0\))
- 贪心计算(\(i\) 从 \(1\) 到 \(n\)):
- 若 \(h[i] > h[i-1]\):
ans += h[i] - h[i-1]
- 若 \(h[i] > h[i-1]\):
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),线性遍历
- 空间复杂度:\(O(n)\),高度数组
-
贪心差分的核心思想:
- 区间覆盖的叠加性:每个操作给区间 \(+1\),多个操作的叠加效果等价于每个位置被覆盖的次数等于其高度
- 上升缺口的必要性:若 \(h_i > h_{i-1}\),位置 \(i\) 比位置 \(i-1\) 高出的部分无法被包含 \(i-1\) 的区间操作覆盖(因为那些操作也会给 \(i-1\) 加 \(1\)),必须单独开启新的区间操作
- 下降段的复用:若 \(h_i \leq h_{i-1}\),位置 \(i\) 的高度可以被覆盖 \(i-1\) 的区间操作顺带覆盖,无需额外操作
- 形式化证明:设 \(d_i = h_i - h_{i-1}\)(\(h_0 = 0\)),则 \(\sum_{i: d_i > 0} d_i = \sum_{i=1}^n \max(0, h_i - h_{i-1})\) 即为最少操作次数
- 适用于"区间增量覆盖序列目标"的贪心问题,核心在于差分思想识别必须单独开启的操作
【算法标签】
普及 #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 100005; // 定义数组最大长度
int n; // 山峰数量
int h[MAX_N]; // 存储山峰高度
int ans; // 存储最终结果(总浇水次数)
int main()
{
// 输入山峰数量
cin >> n;
// 输入每个山峰的高度
for (int i = 1; i <= n; i++)
cin >> h[i];
// 计算需要浇水的最少次数
for (int i = 1; i <= n; i++)
{
// 如果当前山峰比前一个高,需要增加浇水次数
if (h[i] > h[i - 1])
ans += h[i] - h[i - 1];
}
// 输出结果
cout << ans << endl;
return 0;
}
【运行结果】
5
2 3 4 1 2
5
浙公网安备 33010602011771号