UVa 105 The Skyline Problem

题目描述

给定一系列建筑物的位置信息,每个建筑物用一个三元组 (Li,Hi,Ri)(L_i, H_i, R_i)(Li,Hi,Ri) 表示,其中 LiL_iLiRiR_iRi 分别是建筑物的左右边界坐标 (0<Li<Ri0 < L_i < R_i0<Li<Ri),HiH_iHi 是建筑物的高度。所有建筑物都是底部对齐的矩形,且城市视为二维平面。要求根据这些建筑物的轮廓,输出描述整个城市天际线的向量序列。

输入

  • 一系列建筑物的三元组,每行一个。
  • 坐标均为小于 100001000010000 的整数,建筑物数量 1≤n≤50001 \leq n \leq 50001n5000
  • 三元组已按 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 坐标处的最大高度。

  1. 初始化

    • 创建一个长度为 MAXNMAXNMAXN 的数组 grid,初始值为 000
    • 记录所有建筑物中最小左边界 leftMost 和最大右边界 rightMost
  2. 处理输入

    • 对于每个建筑物 (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)
    • 注意:这里取的是闭区间,因为题目中建筑物右边界也是轮廓的一部分。
  3. 生成输出

    • leftMost 开始扫描到 rightMost
    • 当高度发生变化时:
      • 若高度增加,输出当前 xxx 和新高度。
      • 若高度降低,输出前一个 xxx(即 i-1)和新高度。
    • 最后输出 rightMost000,表示轮廓结束。

复杂度分析

  • 时间复杂度:最坏情况下,每个建筑物更新区间长度可达 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 坐标处的最大高度,巧妙地避免了复杂的几何计算。虽然时间复杂度较高,但得益于坐标范围小,依然可以在规定时间内完成。该方法直观易懂,适合作为入门级扫描线问题的练习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值