HDU 1542 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 

 

首先来介绍一下对扫描线的理解。扫描线一般是来解决矩形面积以及周长问题。一般来说,题目给出的矩形比较多,坐标也非常大,所以要进行离散化。

看了看别人写的博客,离散横坐标的居多。

假设有一条线,从下往上或者从上往下扫描整个多边形叠加的区域。这里我们讲从下往上扫描。我们在扫描之前,要用一个结构体来保存好每条边的左端点、右端点以及它的高(要根据高度进行排序),我们用1标记一个矩形的下边,用-1标记一个矩形的上边(这么做的理由是,当扫描线从下往上扫描到下边时,就把这条边的长度加到总区间上,可以理解成扫描线进入了一个矩形;当扫描线扫描到上边时,那就删除这一段线段的长度,可以理解成离开了这个矩形),然后用下一条边的高度减去当前这条边的高度,乘上总区间覆盖的长度,就能得到最终的面积。

 

 

然后提醒一下这个扫描线要注意的问题就是区间的问题
一般的线段树以及我们的区间修改合并,都有一个共同点,就是不会出现区间缺失的现象,什么叫区间缺失,顾名思义,区间缺失就是缺少一些区间没有进行运算,这里的扫描线就会遇到这个问题。
普遍的,我们的线段树以及数据区间分布是这样的:
[1, a][a + 1, b][b + 1, c][c + 1, d][d + 1, e].......
但是如果只是简简单单的用这个来解决扫描线的问题会导致错误,为什么因为,他没有涉及到[a,a + 1],在扫描线中会出现[a,a + 1]中的数据,而常用的线段树的区间概念是无法解决这样的问题的,出现了所谓的区间缺失,怎样解决,下面的代码给出了解决方案,这里简单的提一下,就是利用[ , ),这个区间性质,左闭右开,即可解决区间缺失问题(这段话转自https://blog.csdn.net/qq_18661257/article/details/47622677

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=2005;
int col[maxn<<2];
double x[maxn<<2],sum[maxn<<2];//x数组记录横坐标
struct tree
{
    double l,r,h;//记录上边和下边的左端点、右端点以及高
    int s;
    tree(){};
    tree(double l,double r,double h,double s):l(l),r(r),h(h),s(s){}
    bool operator < (const tree & object)const//根据高度排序
    {
        return h<object.h;
    }
}node[maxn<<2];
void pushup(int l,int r,int rt)
{
    if(col[rt])//这时候说明整段线段被覆盖,直接求这段线段的长度
        sum[rt]=x[r+1]-x[l];//根据左闭右开的性质
    else if(l==r)//当线段是一个点时,长度为0
        sum[rt]=0;
    else//这时候表示这段线段没有被全覆盖,只能合并左右儿子的信息
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l && r<=R)
    {
        col[rt]+=c;
        pushup(l,r,rt);
        return ;
    }
    int m=(l+r)>>1;
    if(L<=m)
        update(L,R,c,l,m,rt<<1);
    if(R>m)
        update(L,R,c,m+1,r,rt<<1|1);
    pushup(l,r,rt);
}
int main()
{
    int cas=1;
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {
        double a,b,c,d;
        int cnt=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            node[cnt]=tree(a,c,b,1);
            x[cnt++]=a;
            node[cnt]=tree(a,c,d,-1);
            x[cnt++]=c;
        }
        int tol=cnt;//因为我们要离散化,所以我们要把边的总数记录下来
        sort(x,x+tol);
        sort(node,node+tol);
        tol=unique(x,x+tol)-x;
        memset(sum,0,sizeof(sum));
        memset(col,0,sizeof(col));
        double ans=0;
        for(int i=0;i<cnt-1;i++)
        {
            int l=lower_bound(x,x+tol,node[i].l)-x;
            int r=lower_bound(x,x+tol,node[i].r)-x-1;
            update(l,r,node[i].s,0,tol-1,1);
            ans+=sum[1]*(node[i+1].h-node[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ans);
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值