• 楼房 洛谷1382 && codevs2995


     P1382 楼房

    题目描述

    地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i])。地平线高度为0。在轮廓线长度最小的前提下,从左到右输出轮廓线。

    下图为样例2。

    输入输出格式

    输入格式:

    第一行一个整数n,表示矩形个数

    以下n行,每行3个整数h[i],l[i],r[i]表示第i个矩形。

    输出格式:

    第一行一个整数m,表示节点个数

    以下m行,每行一个坐标表示轮廓线上的节点。从左到右遍历轮廓线并顺序输出节点。第一个和最后一个节点的y坐标必然为0。

    输入输出样例

    输入样例#1:
    【样例输入1】
    2
    3 0 2
    4 1 3
    
    【样例输入2】
    5
    3 -3 0
    2 -1 1
    4 2 4
    2 3 7
    3 6 8
    
    输出样例#1:
    【样例输出1】
    6
    0 0
    0 3
    1 3
    1 4
    3 4
    3 0
    
    【样例输出2】
    14
    -3 0
    -3 3
    0 3
    0 2
    1 2
    1 0
    2 0
    2 4
    4 4
    4 2
    6 2
    6 3
    8 3
    8 0
    
    

    说明

    【数据范围】

    对于30%的数据,n<=100

    对于另外30%的数据,n<=100000,1<=h[i],l[i],r[i]<=1000

    对于100%的数据,1<=n<=100000,1<=h[i]<=10^9,-10^9<=l[i]<r[i]<=10^9

    两种解法

    这些都是看了学长的博客再写出来的,但是我觉得他写的不够碉,我看不懂,于是我也来写一个

    第一种

    扫描线+堆,我不会扫描线,现学的picture(poj1177),几近崩溃,后来回到这个题上发现这里用到的扫描线也不过如此。

    堆,用的是stl里的multiset,他们说这个可以自己排序,还能当堆使,很神奇

    仔细想想这个题,有很多种情况

    这诸多情况,真是想想就头疼。

    但是扫描线,再加上堆的强大援助就能解决

    下面我就用十分通俗的语言讲解我的思路

    扫描线,就是每一条竖着的线,楼房左侧的线叫入边,右侧的线叫出边,扫描线有长度(高度)up,横坐标x和出入边的标识k(k=1为入边,k=2为出边)。

    struct line{
        int up,x,k;
    }l[200020];

    把所有竖边都加入扫描线后,就进行排序,排序要按照从左到右的顺序

    如果横坐标相同,入边在先,出边在后

    如果同为入边,高的在先,矮的在后,防遮挡

    如果同为出边,矮的在先,高的在后,防遮挡

    int cmp(line i,line j){
        if(i.x!=j.x)return i.x<j.x;
        if(i.k!=j.k)return i.k<j.k;
        if(i.k==1)return i.up>j.up;
        if(i.k==2)return i.up<j.up;
    }

    通过对图形的分析,我们发现,能够参与答案的只有目前的最高点,

    所以我们对于入边只需要堆的最大值,其他的尽管加上

    对于出边只需要判断一下是否是此刻的最大值,然后加加删删

    完整代码

    #include<iostream>
    #include<set>
    #include<algorithm>
    using namespace std;
    int n;
    struct build{
        int h,l,r;
    }a[100010];
    struct line{
        int up,x,k;
    }l[200020];
    int cnt,num;
    struct ANS{
        int ax,ay;
    }ans[400040];
    multiset<int>s;
    int cmp(line i,line j){
        if(i.x!=j.x)return i.x<j.x;
        if(i.k!=j.k)return i.k<j.k;
        if(i.k==1)return i.up>j.up;
        if(i.k==2)return i.up<j.up;
    }
    int main(){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i].h>>a[i].l>>a[i].r;
            l[++cnt].up=a[i].h,l[cnt].x=a[i].l,l[cnt].k=1;
            l[++cnt].up=a[i].h,l[cnt].x=a[i].r,l[cnt].k=2;
        }
        sort(l+1,l+cnt+1,cmp);
        s.insert(0);
        for(int i=1;i<=cnt;i++){
            int mx=*s.rbegin();
            if(l[i].k==1){
                if(l[i].up<=mx)s.insert(l[i].up);
                else{
                    ++num;ans[num].ax=l[i].x;ans[num].ay=mx;
                    ++num;ans[num].ax=l[i].x;ans[num].ay=l[i].up;
                    s.insert(l[i].up);
                }
            }
            if(l[i].k==2){
                if(l[i].up==mx&&s.count(mx)==1){
                    s.erase(mx);
                    ++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<<endl;
        for(int i=1;i<=num;i++){
            cout<<ans[i].ax<<' '<<ans[i].ay<<endl;
        }
    }
    扫描线+堆

    第二种

    线段树,不感兴趣

    /*
      离散化+线段树+模拟
      由于数据量太大,我们先把所有的墙壁离散化,用线段树维护每个离散化后的横坐标的最高点,
      然后模拟求出答案。 
    */
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define lson l,m,now*2
    #define rson m+1,r,now*2+1
    #define M 200010
    using namespace std;
    int mx[M*4],tag[M*4],a[M*2],b[M*2],ans1[M*5],ans2[M*5],n,cnt=1;
    struct node
    {
        int x,y,h;
    };node q[M];
    int read()
    {
        char c=getchar();int num=0,flag=1;
        while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
        while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
        return num*flag;
    }
    void push_up(int now)
    {
        mx[now]=max(mx[now*2],mx[now*2+1]);
    }
    void push_down(int now)
    {
        if(!tag[now])return;
        tag[now*2]=max(tag[now],tag[now*2]);
        tag[now*2+1]=max(tag[now],tag[now*2+1]);
        mx[now*2]=max(tag[now],mx[now*2]);
        mx[now*2+1]=max(tag[now],mx[now*2+1]);
        tag[now]=0;
    }
    void change(int x,int y,int v,int l,int r,int now)
    {
        if(l>=x&&r<=y)
        {
            mx[now]=max(mx[now],v);
            tag[now]=max(tag[now],v);
            return;
        }
        push_down(now);
        int m=(l+r)/2;
        if(m>=x)change(x,y,v,lson);
        if(y>m)change(x,y,v,rson);
        push_up(now);
    }
    int query(int x,int l,int r,int now)
    {
        if(l==r)return mx[now];
        int m=(l+r)/2;
        push_down(now);
        if(x<=m)return query(x,lson);
        else return query(x,rson);
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&q[i].h,&q[i].x,&q[i].y);
            a[i*2-1]=q[i].x;a[2*i]=q[i].y;
        }
        sort(a+1,a+2*n+1);b[1]=a[1];
        for(int i=2;i<=2*n;i++)
          if(a[i]!=a[i-1])b[++cnt]=a[i];
        for(int i=1;i<=n;i++)
        {
            int x=lower_bound(b+1,b+cnt+1,q[i].x)-b;
            int y=lower_bound(b+1,b+cnt+1,q[i].y)-b;
            change(x,y-1,q[i].h,1,cnt,1);
        }
        int tot=0;
        for(int i=1;i<=cnt;i++)
        {
            ans1[i]=b[i];ans2[i]=query(i,1,cnt,1);
            if(ans2[i]!=ans2[i-1])tot++;
        }
        printf("%d
    ",tot*2);
        for(int i=1;i<=cnt;i++)
          if(ans2[i]!=ans2[i-1])
          {
              printf("%d %d
    ",ans1[i],ans2[i-1]);
              printf("%d %d
    ",ans1[i],ans2[i]);
          }
        return 0;
    }
    线段树
  • 相关阅读:
    ASP.NET 无提示关闭窗口
    C# Winform程序获取外网IP地址
    使用System.Drawing.Printing 画报表。
    C# Winform调用IP地址、手机归属和身份证查询接口
    C# 获取文中文字符首字母
    C# WinForm给Button或其它控件添加快捷键响应
    C# 获取农历日期
    TSQL之插入的内容是查询出的值
    C# 获取中文星期的两种方法
    C#批量操作控件
  • 原文地址:https://www.cnblogs.com/thmyl/p/6368116.html
Copyright © 2020-2023  润新知