LeetCode 218. 天际线问题 (轮廓线问题,扫描线思想+multiset可重集操作)

  1. 天际线问题

题意:

城市的天际线是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。现在,假设您获得了城市风光照片(图A)上显示的所有建筑物的位置和高度,请编写一个程序以输出由这些建筑物形成的天际线(图B)。

在这里插入图片描述

-----分割线-----
每个建筑物的几何信息用三元组 [Li,Ri,Hi] 表示,其中 Li 和 Ri 分别是第 i 座建筑物左右边缘的 x 坐标,Hi 是其高度。可以保证 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX 和 Ri - Li > 0。您可以假设所有建筑物都是在绝对平坦且高度为 0 的表面上的完美矩形。

例如,图A中所有建筑物的尺寸记录为:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] 。

输出是以 [ [x1,y1], [x2, y2], [x3, y3], … ] 格式的“关键点”(图B中的红点)的列表,它们唯一地定义了天际线。关键点是水平线段的左端点。请注意,最右侧建筑物的最后一个关键点仅用于标记天际线的终点,并始终为零高度。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。
在这里插入图片描述

例如,图B中的天际线应该表示为:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]。

说明:

任何输入列表中的建筑物数量保证在 [0, 10000] 范围内。
输入列表已经按左 x 坐标 Li 进行升序排列。
输出列表必须按 x 位排序。
输出天际线中不得有连续的相同高度的水平线。例如 […[2 3], [4 5], [7 5], [11 5], [12 7]…] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[…[2 3], [4 5], [12 7], …]

解法:

枚举横坐标x,对于当前x的轮廓线就是所有覆盖x的矩形中y的最大值。

利用扫描线思想
对于矩形[L,R,H],将(L,H)和(R,-H)存入容器,H的正负表示竖线的左右。
当遇到左边时,将高度H插入multiset,
当遇到右边时,从multiset中删除一个H

因为只需要知道关键点,因此不用枚举所有x,只需要枚举竖线横坐标:
先对存竖线的容器排序,然后枚举竖线
对于当前竖线横坐标x,当前轮廓线的高h就是multiset中的最大值

用一个变量last记录上一个关键点的高度,
如果当前竖线横坐标的高度h!=last,说明当前点是新的关键点,那么加入答案

ps:
用multiset存是因为可能有重叠的矩形高度相同,且需要利用multiset的自动排序

code:
class Solution {
public:
    vector<vector<int>> getSkyline(vector<vector<int>>& a) {
        vector<pair<int,int> >temp;
        int len=a.size();
        for(int i=0;i<len;i++){
            temp.push_back({a[i][0],a[i][2]});//左边
            temp.push_back({a[i][1],-a[i][2]});//右边
        }
        sort(temp.begin(),temp.end(),[](pair<int,int>a,pair<int,int>b){
            if(a.first!=b.first)return a.first<b.first;
            if(1LL*a.second*b.second<0){//second符号不同,优先添加
                return a.second>0;
            }else{//second符号相同
                if(a.second<0){//先删小的
                    return -a.second<-b.second;
                }else{//先加大的
                    return a.second>b.second;
                }
            }
        });
        vector<vector<int> >ans;
        multiset<int>s;
        s.insert(0);
        int last=0;//上一个高度
        for(int i=0;i<len*2;i++){
            int x=temp[i].first,y=temp[i].second;
            if(y>0){
                s.insert(y);
            }else{
                s.erase(s.find(-y));//直接s.erase(-y),会删掉所有-y,因此要先find一个-y的迭代器然后再删
            }
            int h=*s.rbegin();
            if(last!=h){
                ans.push_back({x,h});
                last=h;
            }
        }
        return ans;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值