M - Atlantis HDU - 1542 -扫描线-面积并

  • M - Atlantis

  •  HDU - 1542 
  • 感谢:https://www.cnblogs.com/scau20110726/archive/2013/03/21/2972808.html
  • 1.矩形比较多,坐标也很大,所以横坐标需要离散化(纵坐标不需要),熟悉离散化后这个步骤不难,所以这里不详细讲解了,不明白的还请百度
  • 2.重点:扫描线法:假想有一条扫描线,从左往右(从右往左),或者从下往上(从上往下)扫描过整个多边形(或者说畸形。。多个矩形叠加后的那个图形)。如果是竖直方向上扫描,则是离散化横坐标,如果是水平方向上扫描,则是离散化纵坐标。下面的分析都是离散化横坐标的,并且从下往上扫描的
  •  扫描之前还需要做一个工作,就是保存好所有矩形的上下边,并且按照它们所处的高度进行排序,另外如果是上边我们给他一个值-1,下边给他一个值1,我们用一个结构体来保存所有的上下边 
  • 接着扫描线从下往上扫描,每遇到一条上下边就停下来,将这条线段投影到总区间上(总区间就是整个多边形横跨的长度),这个投影对应的其实是个插入和删除线段操作。还记得给他们赋的值1或-1吗,下边是1,扫描到下边的话相当于往总区间插入一条线段,上边-1,扫描到上边相当于在总区间删除一条线段(如果说插入删除比较抽象,那么就直白说,扫描到下边,投影到总区间,对应的那一段的值都要增1,扫描到上边对应的那一段的值都要减1,如果总区间某一段的值为0,说明其实没有线段覆盖到它,为正数则有,那会不会为负数呢?是不可能的,可以自己思考一下)。
  • 每扫描到一条上下边后并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到一块面积,并依此做下去,就能得到最后的面积
  • (这个过程其实一点都不难,只是看文字较难体会,建议纸上画图,一画即可明白,下面献上一图希望有帮组)
  • 注意线段树节点维护的是(i——i+1)一小线段为节点
  • #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 210;
    struct edg
    {
        double l,r,h;
        int flag;
        bool operator<(const edg &b)const
        {
            return h<b.h;
        }
    } edge[maxn*2];
    struct node
    {
        int l,r,lazy;
        double len;
    } tree[maxn*4];
    double ls[maxn*2],ans;
    int cnt,n,NUM,nums;
    void add(double x1,double x2,double y,int id)
    {
        edge[cnt].flag=id;
        edge[cnt].h=y;
        edge[cnt].l=x1;
        edge[cnt++].r=x2;
    }
    void build(int root,int l,int r)
    {
        tree[root].l=l;
        tree[root].r=r;
        tree[root].len=0;
        tree[root].lazy=0;
        if(l==r)return ;
        int mid=(l+r)/2;
        build(root*2,l,mid);
        build(root*2+1,mid+1,r);
    }
    void pushup(int root)
    {
        if(tree[root].lazy)
            tree[root].len=ls[tree[root].r+1]-ls[tree[root].l];
        else if(tree[root].l==tree[root].r)
            tree[root].len=0;
        else tree[root].len=tree[root*2].len+tree[root*2+1].len;
    }
    void updata(int root,int l,int r,int val)
    {
        if(tree[root].l==l&&tree[root].r==r)
        {
            tree[root].lazy+=val;
            pushup(root);
            return ;
        }
        int mid=(tree[root].l+tree[root].r)/2;
        if(l>mid)
            updata(root*2+1,l,r,val);
        else if(mid>=r)
            updata(root*2,l,r,val);
        else
        {
            updata(root*2,l,mid,val);
            updata(root*2+1,mid+1,r,val);
        }
        pushup(root);
    }
    int main()
    {
        while(scanf("%d", &n))
        {
            if(n==0)break;
            ans=cnt=nums=0;
            for(int i=0; i<n; i++)
            {
                double x1,y,x2,y2;
                scanf("%lf%lf%lf%lf",&x1,&y,&x2,&y2);
                add(x1,x2,y,1);
                add(x1,x2,y2,-1);
                ls[nums++]=x1;
                ls[nums++]=x2;
            }
            sort(edge,edge+cnt);
            sort(ls,ls+nums);
            int m=unique(ls,ls+nums)-ls;
            build(1,0, m-1);
            for(int i=0; i<cnt-1; i++)
            {
                int l=lower_bound(ls,ls+m,edge[i].l)-ls;
                int r=lower_bound(ls,ls+m,edge[i].r)-ls;
                updata(1,l,r-1,edge[i].flag);
                ans+=(edge[i+1].h-edge[i].h)*tree[1].len;
            }
            printf("Test case #%d\n", ++NUM);
            printf("Total explored area: %.2f\n\n", ans);
        }
        return 0;
    }

     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值