poj 1151 求矩形面积并 (线段树扫描线)

题意:

给出n个矩形的左下角和右上角坐标,求这n个矩形所构成的面积

思路:

线段树扫描线

这是第一次做到线段树扫描线,刚开始也不懂

如果不懂,可以看:

http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html

和 http://www.faceye.net/search/69289.html

我是看第一个链接弄懂的 然后学习了第二位的方法

代码上也有比较详细的注释,想可以帮到大家

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 205;


struct Line{
    double x;//存的是线段的x坐标
    double y1,y2;//存的是线段的下、上两个端点
    double f;//f是用来记录重叠情况的,可以根据这个来计算node中的c
}line[maxn*2];
//把一段段平行于y轴的线段表示成数组,
//x是线段的x坐标,y1 y2是线段对应的下端点和上端点
//一个矩形,左侧的边的f = 1,右侧的边  f= -1
//f是用来记录重叠情况的,可以根据这个来计算node中的c

double y[maxn*2];//用来记录y坐标,排序之后用来给线段树中的左右实际结点赋值,见代码build部分

//线段树的节点总数,大概要是线段个数的 4 倍(线段树和被操作的数据之间总是四倍关系)
const int maxnode = maxn << 2;
struct IntervalTree{
    double ll[maxnode],rr[maxnode],lf[maxnode],rf[maxnode],cnt[maxnode];
    //ll[]和rr[] 是用来标记线段树某一个区间的 经过离散化之后的 可以投影到实际坐标的 那个左右整点代号
    //lf rf 是用来记录真正的左右坐标的,用来计算该区间的长度
    int c[maxnode];//c是用来标记重叠情况的

    void build(int o, int l, int r){//递归建线段树
        ll[o] = l, rr[o] = r;
        cnt[o] = c[o] = 0;
        lf[o] = y[l], rf[o] = y[r];

        int mid = (l+r)>>1;
        int lc = o << 1, rc = o << 1 | 1;
        if(l + 1 == r) return;
        build(lc, l, mid);
        build(rc, mid, r);
    }
    void calen(int o){//用来计算区间o中的 线段被覆盖的长度
        if(c[o] > 0){
            cnt[o] = rf[o] - lf[o];
            return;
        }
        if(ll[o] + 1 == rr[o]) cnt[o] = 0;
        else{
            cnt[o] = cnt[o<<1] + cnt[o<<1|1];
        }
    }
    void update(int o, Line e){//新加入一条线段后 更新线段树
        double y1 = e.y1, y2 = e.y2;
        if(lf[o] == y1 && y2 == rf[o]){
            c[o] += e.f;
            calen(o);
            return;
        }
        int lc = o << 1, rc = o<<1|1;
        if(y2 <= rf[lc]) update(lc, e);
        else if(y1 >= lf[rc]) update(rc, e);
        else{
            Line tmp = e;
            tmp.y2 = rf[lc];
            update(lc, tmp);
            tmp = e;
            tmp.y1 = lf[rc];
            update(rc, tmp);
        }
        calen(o);
    }
}tree;


bool cmp(Line a, Line b){
    return a.x < b.x;
}
int main(){
    int n;
    int cas = 0;
    while(scanf("%d",&n) != EOF){
        if(n == 0) break;

        double y1,y2,x1,x2;
        int t = 1;
        for(int i = 1; i <= n; i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);

            //这是一个处理和保存矩形左右两条边,并且对坐标进行离散化的过程
            //一个矩形 对应的是两条线段,左面一条(f = 1)右面一条(f = -1)
            line[t].x = x1;
            line[t].y1 = y1;
            line[t].y2 = y2;
            line[t].f = 1;
            y[t] = y1;
            t++;
            line[t].x = x2;
            line[t].y1 = y1;
            line[t].y2 = y2;
            line[t].f = -1;
            y[t] = y2;
            t++;
        }

        sort(y+1, y+t);//对y坐标按照从小到大的顺序排序,这是为了建立线段树,排完序才能离散地分成许多个区间
        sort(line+1, line+t, cmp);//从左到右扫描的话,应该保证在左边的线段先被扫到,
                                  //这里的排序就是让左边的线段在前面(按照x坐标从小到大排序)

        double res = 0;
        tree.build(1,1,t-1);
        //每扫描到一条上下边后并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边
        //的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到一块面积,并依此做下去,
        //就能得到最后的面积。
        tree.update(1,line[1]);//先扫地一条边
        for(int i = 2; i < t; i++){
            res += tree.cnt[1] * (line[i].x - line[i-1].x);//用 高度*覆盖长度
            tree.update(1, line[i]);//扫描当前边
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",++cas,res);
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值