题目描述
给定一系列建筑物的位置信息,每个建筑物用一个三元组 (Li,Hi,Ri)(L_i, H_i, R_i)(Li,Hi,Ri) 表示,其中 LiL_iLi 和 RiR_iRi 分别是建筑物的左右边界坐标 (0<Li<Ri0 < L_i < R_i0<Li<Ri),HiH_iHi 是建筑物的高度。所有建筑物都是底部对齐的矩形,且城市视为二维平面。要求根据这些建筑物的轮廓,输出描述整个城市天际线的向量序列。
输入
- 一系列建筑物的三元组,每行一个。
- 坐标均为小于 100001000010000 的整数,建筑物数量 1≤n≤50001 \leq n \leq 50001≤n≤5000。
- 三元组已按 LiL_iLi 升序排列。
输出
- 一个向量序列,描述从最小 xxx 坐标开始,沿天际线“行走”的路径。
- 向量中奇数位表示 xxx 坐标,偶数位表示高度。
- 序列以高度 000 结束。
示例
输入:
1 11 5
2 6 7
3 13 9
12 7 16
14 3 25
19 18 22
23 13 29
24 4 28
输出:
1 11 3 13 9 0 12 7 16 3 19 18 22 3 23 13 29 0
题目分析
本题可以看作是一个二维平面上的矩形覆盖问题:多个矩形在 xxx 轴上排列,高度不同,我们需要找出它们所形成的外轮廓线。
关键点:
- 每个矩形在 [Li,Ri)[L_i, R_i)[Li,Ri) 区间内贡献一个高度 HiH_iHi。
- 在任意 xxx 坐标处,可见高度为所有覆盖该点的矩形高度的最大值。
- 天际线由这些局部高度变化点构成:当高度发生变化时,就产生一个转折点。
解题思路
方法:离散化 + 扫描线
由于坐标范围较小(< 100001000010000),我们可以直接使用一个数组 grid 来记录每个整数 xxx 坐标处的最大高度。
-
初始化
- 创建一个长度为 MAXNMAXNMAXN 的数组
grid,初始值为 000。 - 记录所有建筑物中最小左边界
leftMost和最大右边界rightMost。
- 创建一个长度为 MAXNMAXNMAXN 的数组
-
处理输入
- 对于每个建筑物 (Li,Hi,Ri)(L_i, H_i, R_i)(Li,Hi,Ri),在区间 [Li,Ri][L_i, R_i][Li,Ri] 内更新
grid[x]为max(grid[x], H_i)。 - 注意:这里取的是闭区间,因为题目中建筑物右边界也是轮廓的一部分。
- 对于每个建筑物 (Li,Hi,Ri)(L_i, H_i, R_i)(Li,Hi,Ri),在区间 [Li,Ri][L_i, R_i][Li,Ri] 内更新
-
生成输出
- 从
leftMost开始扫描到rightMost。 - 当高度发生变化时:
- 若高度增加,输出当前 xxx 和新高度。
- 若高度降低,输出前一个 xxx(即
i-1)和新高度。
- 最后输出
rightMost和 000,表示轮廓结束。
- 从
复杂度分析
- 时间复杂度:最坏情况下,每个建筑物更新区间长度可达 100001000010000,建筑物数量最多 500050005000,总操作次数约为 5000×10000=5×1075000 \times 10000 = 5 \times 10^75000×10000=5×107,在合理优化下可以接受。
- 空间复杂度:O(MAXN)O(MAXN)O(MAXN),即 O(10010)O(10010)O(10010)。
注意事项
- 建筑物右边界坐标 RiR_iRi 处的轮廓高度应取 000,但实际代码中在更新区间时包含了右端点,因此在输出时需注意转折点的位置。
- 输出格式要求以 000 结尾,表示轮廓回到地面。
代码实现
// Skyline Problem
// UVa ID: 105
// Verdict: Accepted
// Submission Date: 2011-11-22
// UVa Run Time: 0.036s
//
// 版权所有(C)2011,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
#define MAXN 10010
int grid[MAXN];
int main (int argc, char const* argv[])
{
int left, height, right, leftMost, rightMost = 0;
bool leftSetted = false;
memset(grid, 0, sizeof(grid));
while (cin >> left >> height >> right)
{
for (int i = left; i <= right; i++)
grid[i] = max(grid[i], height);
if (!leftSetted)
{
leftMost = left;
leftSetted = true;
}
rightMost = max(rightMost, right);
}
cout << leftMost << " " << grid[leftMost];
int current = leftMost;
for (int i = leftMost; i <= rightMost; i++)
{
if (grid[i] == grid[current])
continue;
else
{
if (grid[i] > grid[current])
cout << " " << i << " " << grid[i];
else
cout << " " << (i - 1) << " " << grid[i];
current = i;
}
}
cout << " " << rightMost << " 0\n";
return 0;
}
总结
本题通过直接模拟每个 xxx 坐标处的最大高度,巧妙地避免了复杂的几何计算。虽然时间复杂度较高,但得益于坐标范围小,依然可以在规定时间内完成。该方法直观易懂,适合作为入门级扫描线问题的练习。
1217

被折叠的 条评论
为什么被折叠?



