HDU 1542 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 file 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个矩形的左上角和右下角的坐标,计算多个矩形重叠后的面积

思路:线段树+离散化+扫描线

1.直接离散化因为矩形数量只有最多100个,所以朴素O(n*n*n)也是可以的,判断小矩形是不是在大矩形内

假设输入的矩阵中共有num1个不同的x坐标和num2个不同的y坐标,那么整个二维平面就被分割成了(num1-1)*(num2-1)个小方格矩形. 我们只要看上面(num1-1)*(num2-1)个小方格矩形哪些被某个大矩形覆盖,哪些没有被任何一个大矩形覆盖即可.我们令mp[i][j]=1表示以(x[i], y[j])点为左上角,以(x[i+1], y[j+1])点为右上角的那个小矩形被某个大矩形覆盖了. 假设大矩形a[i]的x坐标范围在[xi,xj]之间而y坐标在[yk,yh]之间.那么该大矩阵I肯定使得 所有的mp[a][b]==1. 其中i<=a<k且j<=b<h.最终我们只要把那些mp值==1的小矩形面积加起来即可.来源

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=200+5;
struct Node//矩形
{
    double x1,y1,x2,y2;
}nodes[maxn];
double x[maxn],y[maxn];
bool mp[maxn][maxn];
 
int find(double *x,double val,int n)//在数组x中找到val值的位置
{
    int L=0,R=n-1;
    while(R>=L)
    {
        int mid=L+(R-L)/2;
        if(x[mid]==val) return mid;
        else if(x[mid]>val) R=mid-1;
        else L=mid+1;
    }
    return -1;
}
 
int main()
{
    int n,num1,num2,kase=0;
    while(scanf("%d",&n)==1 && n)
    {
        num1=num2=0;//num1记录有多少个不同x值,num2记录y的
        memset(mp,0,sizeof(mp));
        for(int i=0;i<n;++i)
        {
            scanf("%lf%lf%lf%lf",&nodes[i].x1,&nodes[i].y1,&nodes[i].x2,&nodes[i].y2);
            x[num1++]=nodes[i].x1;
            x[num1++]=nodes[i].x2;
            y[num2++]=nodes[i].y1;
            y[num2++]=nodes[i].y2;
        }
        sort(x,x+num1);
        sort(y,y+num2);
        num1=unique(x,x+num1)-x;//去重
        num2=unique(y,y+num2)-y;//去重
 
        for(int i=0;i<n;++i)
        {
            //找出第i个原始大矩形覆盖的小矩形范围
            int L_x=find(x,nodes[i].x1,num1);
            int R_x=find(x,nodes[i].x2,num1);
            int L_y=find(y,nodes[i].y1,num2);
            int R_y=find(y,nodes[i].y2,num2);
 
            for(int j=L_x;j<R_x;++j)
            for(int k=L_y;k<R_y;++k)
                mp[j][k]=true;
        }
        double ans=0;
        for(int i=0;i<num1;++i)
        for(int j=0;j<num2;++j)if(mp[i][j])
            ans += (x[i+1]-x[i])*(y[j+1]-y[j]);
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++kase,ans);
    }
 
    return 0;
}

2.线段树+离散化+扫描线(好难理解QAQ)时间复杂度O(n*logn)

扫描线

对一个四元组(x,y1.y2,k) 在[y1,y2-1]这个区间上执行修改,该区间的线段树
被划分为O(logN)个节点,每个节点的cnt+k
对于线段树中任意一个节点[l,r] 若cnt>0 则len等于a[rt].rf-a[rt].lf,否则等于
两个子节点的len和,在一个节点的cnt被修改时,已经线段树从下往上传递信息时
都依据此方法。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <cmath>
using namespace std;
#define MAXN 205
struct Node
{
    int l,r;//线段树的左右整点
    int cnt;//cnt用来记录重叠情况
    double len,lf,rf;//
    //len用来计算实在的长度,rf,lf分别是对应的左右真实的浮点数端点
}a[MAXN*3];
struct Line
{
    double x,y1,y2;
    int flag;
}line[MAXN];
//把一段段平行于y轴的线段表示成数组 ,
//x是线段的x坐标,y1,y2线段对应的下端点和上端点的坐标
//一个矩形 ,左边的那条边f为1,右边的为-1,
//用来记录重叠情况,可以根据这个来计算,nod节点中的c

bool cmp(Line a,Line b)//sort排序的函数
{
    return a.x < b.x;
}

double y[MAXN];//记录y坐标的数组
void Build(int rt,int l,int r)//构造线段树
{
    a[rt].l=l;a[rt].r=r;
    a[rt].len=a[rt].cnt=0;
    a[rt].lf=y[l];
    a[rt].rf=y[r];
    if(l+1==r)  return;
    int mid=(l+r)>>1;
    Build(rt<<1,l,mid);
    Build(rt<<1|1,mid,r);//递归构造
}
void query(int rt)//计算长度
{
    if(a[rt].cnt>0)
    {
        a[rt].len=a[rt].rf-a[rt].lf;
        return;
    }
    if(a[rt].l+1==a[rt].r) a[rt].len=0;
    else  a[rt].len=a[rt<<1].len+a[rt<<1|1].len;
}
void update(int rt,Line e)//加入线段e,后更新线段树
{
    if(e.y1==a[rt].lf&&e.y2==a[rt].rf)
    {
        a[rt].cnt+=e.flag;
        query(rt);
        return;
    }
    if(e.y2<=a[rt<<1].rf)  update(rt<<1,e);
    else if(e.y1>=a[rt<<1|1].lf)  update(rt<<1|1,e);
    else
    {
        Line tmp=e;
        tmp.y2=a[rt<<1].rf;
        update(rt<<1,tmp);
        tmp=e;
        tmp.y1=a[rt<<1|1].lf;
        update(rt<<1|1,tmp);
    }
    query(rt);
}
int main()
{
    int i,n,t,iCase=0;
    double x1,y1,x2,y2;
    while(scanf("%d",&n),n)
    {
        iCase++;
        t=1;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[t].x=x1;
            line[t].y1=y1;
            line[t].y2=y2;
            line[t].flag=1;
            y[t]=y1;
            t++;
            line[t].x=x2;
            line[t].y1=y1;
            line[t].y2=y2;
            line[t].flag=-1;
            y[t]=y2;
            t++;
        }
        t--;
        sort(line+1,line+t+1,cmp);
        sort(y+1,y+t+1);
        Build(1,1,t);
        update(1,line[1]);
        double res=0;
        for(i=2;i<=t;i++)
        {
            res+=a[1].len*(line[i].x-line[i-1].x);
            update(1,line[i]);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",iCase,res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/deepseazbw/article/details/81134235
今日推荐