【codevs 3044 矩形面积合并】【poj 1151 Atlantis】【hdu 1542 Atlantis】题意&题解&代码(c++)

来源:互联网 发布:平板电脑windows推荐 编辑:程序博客网 时间:2024/06/06 18:08

* Atlantis*(矩形面积合并)


Time Limit:1000MS Memory Limit:10000KB

Description

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个矩形,求他们总共占地面积(也就是求一下面积的合并)

输入描述
可能有多组数据,读到n=0为止

每组数据第一行一个数n,表示矩形个数(n<=100)

接下来n行每行4个实数x1,y1,x2,y1(0 <= x1 < x2 <= 100000;0 <= y1 < y2 <= 100000),表示矩形的左下角坐标和右上角坐标

输出描述
每组数据输出一行表示答案

注:不同网站输出格式不同


题解:
用线段树来求解,为线段树中的扫描线

因为所给线为实数,所以应当先离散化,即hash

顾名思义,扫描法就是用一根想象中的线扫过所有矩形,在扫的过程中将矩形分成多个小块,计算出小块矩形的面积,最后进行面积加和。

任意选取一个轴建立线段树,则矩形与选取的轴平行的两条边是没有用的,直接去掉。

假定选则y轴建树:

现想象有一条线从最左边的边开始依次向右扫描。线段树用来维护当前覆盖在y轴上的线段的总长度,初始时总长度为0。用tree.【len】来保存线段长(实际)总和,初始时为0。

由左往右扫描,扫描到矩形的边时先计算小块的面积,再将它插入线段树,扫描到矩形的右边时将左边从线段树中删除。而在代码中实现的方法就是,每条边都有一个id变量,左为1,右边为-1。

注:计算面积方法——每次扫到一个新的横坐标时,意味着小矩形的长为目前覆盖着长度的总和tree[1].len,小矩形的宽为新扫描到的线与上一条线的距离(即x坐标与上一的x坐标之差x[i]-x[i-1]),乘积即小矩形的面积。

代码如下:

#include<iostream>#include<algorithm>#include<stdio.h>#include<map>#define lson id*2#define rson id*2+1  using namespace std;map<double,int>hashx;//表示每一个坐标对应的hash后的值 map<double,int>hashy;//表示每一个坐标对应的hash后的值 map<int,double>mpx;//表示hash后的值对应的实际坐标 map<int,double>mpy;//表示hash后的值对应的实际坐标 struct edge1{    int val; //记录这个点所表示的区间是否被线段所覆盖     double len;//记录节点所代表区间已经被覆盖的长度 }tree[1000];//注意此时每一个叶子节点要想象成个长度为1的区间//例如原本应代表位置1的节点此时应代表1~2的区间 struct edge2{    double xnow,yup,ydown;    //xnow表示它实际的横坐标     //yup与ydown分别表示它的两个纵坐标     int id;//表示是矩形的靠前的边或是靠后的边 }s[1000];double px[1000],py[1000];//用于hash int cmp(edge2 a,edge2 b){    if (a.xnow!=b.xnow)    return a.xnow<b.xnow;    else  if (a.ydown!=b.ydown)    return a.ydown<b.ydown;    else     return a.yup<b.yup; }//按x排序矩形的边 //线段树内容 void push_up(int id){    tree[id].len=tree[lson].len+tree[rson].len;    tree[id].val=tree[lson].val+tree[rson].val;    return ;}void build_tree(int id,int l,int r){    if (l==r)    {        tree[id].val=0;        tree[id].len=0;        return ;    }    int mid=(l+r)/2;    build_tree(lson,l,mid);    build_tree(rson,mid+1,r);    push_up(id);    return ;}void add_tree(int id,int l,int r,int L,int R,int v){    if (l==r&&l>=L&&r<=R)    {        tree[id].len=0;        tree[id].val+=v;        if (tree[id].val>0)        tree[id].len=mpy[l+1]-mpy[l];         //这里加1的原因是此时的l代表的是l~l+1的这个区间      return ;    }    int mid=(l+r)/2;    if (mid>=L)    add_tree(lson,l,mid,L,R,v);    if (mid+1<=R)    add_tree(rson,mid+1,r,L,R,v);    push_up(id);    return ;}//线段树完 int n,t=0;double x1,x2,y1,y2;int main(){    while(cin>>n)    {        if (n==0)        return 0;        t++;//hash开始         for (int i=1;i<=n;i++)        {            cin>>x1>>y1>>x2>>y2;            s[i].xnow=x1;            s[i].yup=y2;            s[i].ydown=y1;            s[i].id=1;            s[i+n].xnow=x2;            s[i+n].yup=y2;            s[i+n].ydown=y1;            s[i+n].id=-1;            px[i]=x1;            px[i+n]=x2;            py[i]=y1;            py[i+n]=y2;        }        sort(px+1,px+1+2*n);//注意此处是2*n,因为 每一个矩形存了两个横坐标         sort(py+1,py+1+2*n);        int numx=0,numy=0;        for (int i=1;i<=2*n;i++)        {            if (px[i]!=px[i+1])            {                numx++;                hashx[px[i]]=numx;                mpx[numx]=px[i];            }            if (py[i]!=py[i+1])            {                numy++;                hashy[py[i]]=numy;                mpy[numy]=py[i];            }        }   //hash结束         sort(s+1,s+1+2*n,cmp);//注意2*n //      for (int i=1;i<=2*n;i++)//      {//                  cout<<s[i].xnow<<endl;//          cout<<s[i].yup<<' '<<s[i].ydown<<endl;//          cout<<hashy[s[i].yup]<<' '<<hashy[s[i].ydown]<<endl;////      }        build_tree(1,1,n);        double ans=0.0;        for (int i=1;i<=2*n;i++)        {            //每次到达一个新的x坐标时先计算再加边             ans+=tree[1].len*(s[i].xnow-s[i-1].xnow);            while(s[i].xnow==s[i+1].xnow)            {                add_tree(1,1,numy,hashy[s[i].ydown],hashy[s[i].yup]-1,s[i].id);                //要让 hashy[s[i].yup]减1是因为每一个节点原本所代表的位置x被当做x~x+1                 i++;            }            add_tree(1,1,numy,hashy[s[i].ydown],hashy[s[i].yup]-1,s[i].id);        }        printf("Test case #%d\n",t);        printf("Total explored area: %.2f\n\n",ans);//poj上的输出格式很坑           }}

可看sgu 128 与本题应用到同样的扫描线。

1 0
原创粉丝点击