1 条题解
-
0
UOI 2020 Stage 4 Day1 - 摩天大楼 题解
题目概述
科扎克·武斯需要建造 n 座摩天大楼,这些大楼必须位于同一条直线上,且他的大楼在最左边。第 i 座大楼距离他的大楼 i 公里,高度为 a_i 层,每层美丽值为 b_i。
关键规则:一座大楼的第 i 层只有在它与科扎克大楼之间没有其他大楼也拥有第 i 层时,才会被看到。
目标:通过合理安排大楼的建造顺序,使得从科扎克大楼能看到的所有楼层的美丽值之和最大化。
问题分析
核心观察
- 可见性条件:对于任意高度 h,只有距离最近的高度 ≥ h 的大楼的第 h 层是可见的
- 单调栈思想:这实际上是一个在高度维度上的"最近更大值"问题
- 贪心策略:我们应该优先安排美丽值高的大楼在关键位置
数学模型
将问题重新表述:
- 我们需要为每个高度 h (1 ≤ h ≤ max_a) 分配一个大楼
- 对于高度 h,我们选择美丽值最大的可用大楼
- 但约束是:如果选择了高度为 a 的大楼,那么它贡献的可见楼层是从某个起始高度到 a
解题思路
贪心算法
- 按美丽值排序:将大楼按美丽值 b 降序排序,美丽值相同时按高度 a 降序排序
- 维护当前最大高度:记录已经覆盖的最大高度 max_h
- 遍历处理:对于每个大楼,如果它的高度大于当前最大高度,则:
- 计算新增的可见楼层数:(a_i - max_h)
- 这些新增楼层的美丽值都是 b_i
- 更新总美丽值和最大高度
算法正确性证明
为什么这样贪心是正确的?
- 美丽值优先:由于 b_i 是每层的美丽值,我们希望尽可能多的高层使用大的 b_i
- 高度利用:当处理美丽值大的大楼时,我们希望它贡献尽可能多的新楼层
- 无后效性:后面的选择不会影响前面已经确定的可见楼层
算法实现
#include <iostream> #include <vector> #include <algorithm> using namespace std; typedef long long ll; struct Building { ll a, b; }; // 排序规则:按b降序,若b相同则按a降序 bool cmp(const Building& x, const Building& y) { if (x.b != y.b) { return x.b > y.b; } return x.a > y.a; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<Building> buildings(n); for (int i = 0; i < n; ++i) { cin >> buildings[i].a; } for (int i = 0; i < n; ++i) { cin >> buildings[i].b; } sort(buildings.begin(), buildings.end(), cmp); ll max_h = 0; ll total = 0; for (auto& bld : buildings) { if (bld.a > max_h) { total += (bld.a - max_h) * bld.b; max_h = bld.a; } } cout << total << endl; return 0; }复杂度分析
- 时间复杂度:O(n log n),主要来自排序操作
- 空间复杂度:O(n),用于存储大楼信息
样例分析
样例1
输入: 4 2 1 3 4 4 2 1 3 处理过程: 排序后:(a=2,b=4), (a=4,b=3), (a=1,b=2), (a=3,b=1) - 处理(2,4):新增高度1-2,贡献2×4=8 - 处理(4,3):新增高度3-4,贡献2×3=6 - 其他大楼不增加新高度 总美丽值:8+6=14样例2
输入: 6 1 10 3 9 8 2 8 3 2 4 5 6 处理过程: 排序后按b降序处理,计算新增高度贡献 最终得到总美丽值51总结
这道题的关键在于将复杂的可见性条件转化为简单的贪心选择问题。通过按美丽值排序并维护当前达到的最大高度,我们可以高效地计算出最大总美丽值。
核心技巧:
- 识别出这是一个"按美丽值分配高度区间"的问题
- 使用排序贪心策略
- 维护当前覆盖的高度范围
这种贪心思路在很多区间分配问题中都有应用,是竞赛中的经典技巧。
- 1
信息
- ID
- 4091
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者