POJ - 1151 Atlantis 扫描线求重叠面积

There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
Input
The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0 <= x1 < x2 <= 100000;0 <= y1 < y2 <= 100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.
The input file is terminated by a line containing a single 0. Don’t process it.
Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.
Output a blank line after each test case.
Sample Input
2
10 10 20 20
15 15 25 25.5
0
Sample Output
Test case #1
Total explored area: 180.00

扫描线的裸题了,题意很简单:就是给你N个矩形,问你覆盖后,最后的面积是多少,重复部分只算一次。
其实过程很简单,我简要阐述一下扫描线的工作流程。
这里写图片描述
扫描线,顾名思义,扫描嘛,这里举例从下往上扫描。
这里我们用线段树维护x的距离和。
开始碰到GG这条线,我们就是加上个[G,F]的区间,此时x的总和就只有GF,加上它到下一个线的距离差*x总距离和
然后继续扫描碰到DC,加上这个区间,因为是被覆盖的,所以此时x距离和仍是GF+JC, 这时加上它到下一个线的距离差*x总距离和
进行下一步,此时碰到HE,这条边是出边,所以此时GF区间被删去。再次进行刚才的计算。

重复这个过程就能得到最后的解了。

不过这个题有好多种做法,不一定非要线段树,只要能维护区间就行。不过线段树是比较适用的。
第一种做法,是将坐标全部离散到[1,2*N],排序存起来,需要找对应的坐标节点就直接二分就可以了

代码如下:

#include<iostream>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<cstring>
#include<cmath>
using namespace std;
const int MAX = 510;
const double eps = 1e-6;
#define lson (k*2)
#define rson (k*2+1)
class Node{
public:
    int l,r;
    int s;
    double sum;
    int mid(){
        return (l+r)/2;
    }
};
Node tree[MAX*4];
class Line{
public:
    double lx,rx,y;
    int s;
    Line();
    Line(double _lx,double _rx,double _y,int _s);
};
Line line[MAX*4];
double pos[MAX*4];
bool cmp(Line A,Line B){
    if(B.y - A.y > eps)
        return true;
    return false;
}
/* 
这里建立线段树是[l,mid],[mid,r],因为如果是[l,mid],[mid+1,r]这样会漏掉[mid,mid+1]这部分
*/
void Build(int L,int R,int k){
    tree[k].l = L;tree[k].r = R;
    tree[k].s = 0;tree[k].sum = 0;
    if(L + 1 == R)  return;//这里就是判断叶子节点了。
    int m = (L+R)/2;
    Build(L,m,lson);
    Build(m,R,rson);
}
void PushUp(int k){
    if(tree[k].s > 0){//如果此时存在入边,那么这个区间就这有这条边的范围。即使多条也是直接覆盖
        tree[k].sum = pos[tree[k].r] - pos[tree[k].l];
    }
    else if(tree[k].l + 1 == tree[k].r){//如果此时不存在入边了。而且是一个单独的叶子节点,即单个端点,直接诶更为0
        tree[k].sum = 0;
    }
    else{//如果不是叶子节点,就直接向上传递。
        tree[k].sum = tree[lson].sum + tree[rson].sum;
    }
}
//把每条线都插入,相等于在这个区间进行更新
void Insert_Line(int L,int R,int k,int s){
    if(L <= tree[k].l && tree[k].r <= R){
        tree[k].s += s;
        PushUp(k);
        //这里相当于进行一次需要的更新
        return;
    }
    int M = tree[k].mid();
    if(L < M)   Insert_Line(L,R,lson,s);
    if(R > M)   Insert_Line(L,R,rson,s);
    PushUp(k);
}
int main(void){
    int N;
    double x1,y1,x2,y2;
    int Case = 0;
    while(scanf("%d",&N) != EOF){
        if(N == 0)  break;
        for(int i=1;i<=N;++i){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[i] = Line(x1,x2,y1,1);
            line[N+i] = Line(x1,x2,y2,-1);
            pos[i] = x1;pos[N+i] = x2;
        }
        sort(pos+1,pos+1+2*N);
        sort(line+1,line+1+2*N,cmp);
        Build(1,2*N,1);
        double res = 0;


        for(int i=1;i<2*N;++i){
            //通过二分来确定每个节点的位置
            int l = lower_bound(pos+1,pos+1+2*N,line[i].lx)-pos;
            int r = lower_bound(pos+1,pos+1+2*N,line[i].rx)-pos;
            Insert_Line(l,r,1,line[i].s);
            res += (line[i+1].y-line[i].y)*tree[1].sum;
        }
        printf("Test case #%d\n",++Case);
        printf("Total explored area: %.2f\n\n",res);
    }


    return 0;
}

Line::Line(){
    lx = rx = y = 0;
    s = 0;
}
Line::Line(double _lx,double _rx,double _y,int _s){
    lx = _lx;
    rx = _rx;
    y = _y;
    s = _s;
}

第二种就是不离散,直接节点存double,不过这样插入的时候,递归需要一些变化,因为左右节点并不是均分的。所以要改变一写。
代码如下:

#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>


using namespace std;
const int MAX = 1100;
const double eps = 1e-6;
#define lson (k*2)
#define rson (k*2+1)
class Node{
public:
    int l,r;
    int cnt;
    double sum;
    double lx,rx;
    int mid(){
        return (l+r)/2;
    }
};
class Line{
public:
    double x1,x2,y;
    int s;
    Line();
    Line(double _x1,double _x2,double _y,int _s);
};
Node tree[MAX*4];
Line q[MAX*4];
double posX[MAX*4];
bool cmp(Line A,Line B){
    if(B.y - A.y > eps)
        return true;
    else
        return false;
}
void Build(int L,int R,int k){
    tree[k].l = L;tree[k].r = R;
    tree[k].lx = posX[L];
    tree[k].rx = posX[R];
    tree[k].sum = 0;
    if(L + 1 == R)  return;
    int mid = tree[k].mid();
    Build(L,mid,lson);
    Build(mid,R,rson);
}
void PushUp(int k){
    if(tree[k].cnt){
        tree[k].sum = tree[k].rx-tree[k].lx;
    }
    else if(tree[k].l + 1 == tree[k].r){
        tree[k].sum = 0;
    }
    else{
        tree[k].sum = tree[lson].sum + tree[rson].sum;
    }
}
void Update(int k,Line v){
    if(v.x1 == tree[k].lx && v.x2 == tree[k].rx){
        tree[k].cnt += v.s;
        PushUp(k);
        return;
    }
    if(v.x2 <= tree[lson].rx)  Update(lson,v);
    else if(v.x1 >= tree[rson].lx) Update(rson,v);
    else{
        Line temp = v;
        temp.x2 = tree[lson].rx;
        //这里要继续查询左边孩子,所以要把查询区间修改为左孩子区间
        Update(lson,temp);
        temp = v;
        temp.x1 = tree[rson].lx;
        //这里同理
        Update(rson,temp);
    }
    PushUp(k);
}
int main(void){
    int N;
    int Case = 0;
    while(scanf("%d",&N) != EOF){
        if(N == 0)  break;
        double x1,y1,x2,y2;
        for(int i=1;i<=N;++i){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            q[i] = Line(x1,x2,y1,1);
            posX[i] = x1;
            q[N+i] = Line(x1,x2,y2,-1);
            posX[N+i] = x2;
        }
        sort(posX+1,posX+2*N+1);
        sort(q+1,q+2*N+1,cmp);
        Build(1,2*N,1);
        Update(1,q[1]);
        double res = 0;
        for(int i=2;i<=2*N;++i){
            res += (q[i].y-q[i-1].y)*tree[1].sum;
            Update(1,q[i]);
        }
        printf("Test case #%d\n",++Case);
        printf("Total explored area: %.2f\n\n",res);
    }

    return 0;
}

Line::Line(){
    x1 = x2 = y = 0;
    s = 0;
}
Line::Line(double _x1,double _x2,double _y,int _s){
    x1 = _x1;
    x2 = _x2;
    y = _y;
    s = _s;
}

猜你喜欢

转载自blog.csdn.net/zhao5502169/article/details/81486452