HDU 1542:Atlantis


Atlantis

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 12377    Accepted Submission(s): 5195

点击打开题目链接
Problem 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 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个矩形,求他们面积的并。

 

解题思路:看了别人的东西,然后回来研究了一下。

解这个题目用知识是线段树。感觉很神奇。思想真的不知道怎么来说,看了别人的也没觉得就都讲的很好,就直接晒代码,再让

大脑跟着运行一下。下面是一下午的收获。

 

解决题目的关键是先定位矩形的横坐标x,然后取其区间为(y1,y2).并将所有的纵坐标存起来放在数组中。排序一下,找出最小纵坐标和

最大纵坐标,这个最大的线段区间就是线段树的根,然后根据存放纵坐标的数组构建线段树。(感觉这个很难去表述思路,只可意会,不可

言传的感觉,明白了又说不出)。。。

上代码,和自己用脑运行代码的过程吧。代码看完不要走开哦,后面有参照代码的求解思路。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>

const int MAXN = 110;

using namespace std;
int n;
double Y[2*MAXN];
struct Line
{
    double x;       ///横坐标的值
    double y1,y2;   ///纵坐标的区间
    int flag;       ///flag等于1时,代表它是长方形左边,flag等于-1时,代表它是长方形右边
}line[2*MAXN];
struct NODE         ///线段树节点类型
{
    double x;       ///最新到过该节点的线对应的x(横坐标的值)
    double left,right;       ///左边界,右边界
    bool sign;               ///用来标记该节点是否叶子节点
    int cover;               ///覆盖值
}node[MAXN<<3];    
///这里不能使MAXN左移两位,因为有N个矩形,对应2*N个线段,则应该是2*N左移2位。
bool cmp(struct Line L1,struct Line L2)
{
    return L1.x < L2.x;
}
void Build(int rt,int l,int r)  ///l,r是Y数组的下标
{
    node[rt].left = Y[l];
    node[rt].right = Y[r];
    node[rt].x = -1;
    node[rt].sign = false;      ///flase说明这个节点不是叶子节点
    node[rt].cover = 0;
    if(l+1 == r)
    {
        node[rt].sign = true;   ///叶子节点
        return;
    }
    int mid = (l+r)>>1;
    Build(rt<<1,l,mid);    ///递归构建左子树
    Build(rt<<1|1,mid,r);  ///递归构建右子树
}
///参数含义,rt为节点,line_x代表当前线对应的横坐标,l下界,r上界
///flag代表当前的线是长方形的左侧的边还是右侧的边
double Calculate(int rt,double line_x,double l,double r,int flag)
{
    if(r<=node[rt].left || l>=node[rt].right)
        return 0;
    if(node[rt].sign)  ///代表其是叶子节点
    {
        if(node[rt].cover>0)
        {
            double pre = node[rt].x;
            double ans = (line_x-pre)*(node[rt].right-node[rt].left);
            node[rt].x = line_x;
            node[rt].cover += flag;
            return ans;
        }
        else
        {
            node[rt].x = line_x;
            node[rt].cover += flag;
            return 0;
        }
    }
    double ans1 = Calculate(rt<<1,line_x,l,r,flag);
    double ans2 = Calculate(rt<<1|1,line_x,l,r,flag);
    return ans1+ans2;
}
int main()
{
    int Case = 0;
    double x1,y1,x2,y2;
    while(~scanf("%d",&n))
    {
        if(n == 0)
            break;
        int i,j;
        j = 0;
        for(i = 0; i < n; i++)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            Y[j] = y1;
            line[j].x = x1;     ///长方形左侧的边
            line[j].y1 = y1;
            line[j].y2 = y2;
            line[j].flag = 1;
            j++;
            Y[j] = y2;
            line[j].x = x2;     ///长发型右侧的边
            line[j].y1 = y1;
            line[j].y2 = y2;
            line[j].flag = -1;
            j++;
        }
        sort(Y,Y+j);            ///对纵坐标进行从小到大排序。
        sort(line,line+j,cmp);  ///对线的横坐标进行从小到大排序
        Build(1,0,j-1);         ///构建线段树
        double union_area = 0;  
        for(i = 0; i < j; i++)
        {
            union_area += Calculate(1,line[i].x,line[i].y1,line[i].y2,line[i].flag);
        }
        printf("Test case #%d\n",++Case);
        printf("Total explored area: %.2lf\n\n",union_area);
    }
    return 0;
}

以测试数据为例,求解代码的运行过程。
首先要构建线段树,构建好的线段树和排好序的直线如下图所示,其flag值代表这条线段是矩形的左侧边(用1表示)还是右侧的边(用-1)表示。



接下来按照代码,考虑第一条线段。就是图中红色线段。递归过程其在线段树中的走向如树种红色线段所示。


接下来考虑第2条线段,就是图中的蓝色线段,递归过程在线段树中的走向如蓝色线段所示。

注意注意注意!!!!!。上面图中的写错了,应该是节点2和节点4的x的值变为10,其对应的flag值都等于1.



再考虑第三条线段,就是坐标系中绿色的线段。其递归过程在线段树中的走向如绿色线段所示。


然后考虑最后一条线段,褐色线段,其走向如图褐色线段所示:


最后总结一下,每次调用求的面积图示:


  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值