楼房

思路

属于扫描线算法思想,通过动态维护当前高度集合,利用最大高度(天际线)来不断动态变化,同时记录轮廓拐点,最后顺序输出。

题解

#include <bits/stdc++.h>
using namespace std;
int n,cnt,num   ;
struct build
{
    int h,l,r;
}a[100010]; //楼房
struct line
{
    int up,x,k; //up代表线高度(楼房左右边线),k代表出入属性,1代表入,2代表出
}l[200020];
struct ANS
{
    int ax,ay;
}ans[400040]; //储存答案的交叉点
int cmp(line a,line b)
{
    if(a.x != b.x) return a.x<b.x;
    if(a.k != b.k) return a.k<b.k;//入度优先
    // 区分入度出度考虑高度优先级
    if(a.k == 1) return a.up > b.up; //入度,高的优先
    if(a.k == 2) return a.up < b.up; //出度,矮的优先
}
multiset<int> s;
int main()
{
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        cin >> a[i].h >> a[i].l >> a[i].r;
        l[++cnt] = {a[i].h,a[i].l,1}; //楼左,入度
        l[++cnt] = {a[i].h,a[i].r,2}; //楼右,出度
    }
    s.insert(0); //初始化最高为0
    sort(l+1,l+cnt+1,cmp);

    for(int i=1;i<=cnt;i++)
    {
        int m = *s.rbegin(); //存最大高度
        if(l[i].k==1)
        {
            if(l[i].up <= m) s.insert(l[i].up);
            else
            {
                ++num;ans[num].ax=l[i].x;ans[num].ay=m;
                ++num;ans[num].ax=l[i].x;ans[num].ay=l[i].up;
                s.insert(l[i].up);
            }
        }
        else if(l[i].k == 2)
        {
            if(l[i].up==m && s.count(m)==1)
            {
                s.erase(m); //移除该高度(存交叉点),继续下
                ++num;ans[num].ax=l[i].x;ans[num].ay=l[i].up;
                ++num;ans[num].ax=l[i].x;ans[num].ay=*s.rbegin();
            }
            else s.erase(s.find(l[i].up));
        }
    }
    cout << num << '\n';
    for(int i=1;i<=num;i++) cout << ans[i].ax << ' ' << ans[i].ay << '\n';
    return 0;
}

这里代码借鉴(其实是抄的)了题解shuri001佬的代码,写得太好了,叹为观止。

我看题解中还有一些用的线段树,可以学学,留。