您的位置:首页 > 其它

ACM: 线段树 poj 1151 足足想了三…

2016-05-19 23:23 435 查看
Atlantis

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

题意: 计算矩形的面积, 矩形之间重叠的部分只能算一次.

解题思路:

1. 在坐标范围中, 一般的方法都是"离散化": 将出现过的点, 把平面分成网格形状, 每个矩形大小不一.

2. 思路同黑书上"金矿"思路相似, 只需要从左到右每次将一条垂直与X轴的竖边插入线段树中,

并且计算当前前后两条竖边夹的面积, 当然夹的面积可能为0.

3. 先将输入的矩形的两条竖边保存, 并且按照X的大小从左到右排序. 以便从左到右插入. 而且,

矩形的Y坐标保存起来从小到大排序, 为建树准备区间范围.        

线段树分析:     

建树: 插入的单位是一条竖边, 是对Y坐标进行二分建树. 并且我们设置一个flag标记来判断,

矩形的进入和退出, 分别是+1和-1. 因为事先我们都知道全部的边的范围.           

举个例子: 用题目给出的例子. 如下图:



      树是建好了,怎么使用它来帮助我们计算面积呢?两条竖边怎么计算矩形面积?

每两条矩形的长可以简单的line[i].x-line[i-1].x即可.(X轴差值). 那么矩形的高,

我们就要在线段树每个区间设置一个变量len, 表示每插入一条边之后当前夹成的矩形的宽.

                    分成三个矩形计算面积了.

      插入竖边: 每次插入时如果当前区间被完全覆盖,那么就要对 flag 进行更新入边 +1 出边 -1,

更新后判断当前节点的 flag 是否大于 0, 如果大于 0,那么当前节点的 len就是节点

所覆盖的区间. 否则,如果是叶子节点,则 len=0,如果内部节点,则 len=左右儿子的 len 之和.

(这个过程可以形象看成是上面1,2,3三个矩形, 插入和删除过程).

4. 收获不少这题. 线段树+离散化.

代码:

#include <cstdio>

#include <iostream>

#include <cstring>

#include <cstdlib>

using namespace std;

#define MAX 1005

struct Line

{

    double x, y1, y2;

    int flag;

}line[MAX];

struct node

{

    int left, right;

    double l, r, len;

    int flag;

}pt[MAX];

int n;

double x1, x2, y1, y2;

int num;

double y[MAX];

int cmp1(const void *a, const void *b)

{

    if(*(double *)a > *(double *)b) return 1;

    else return -1;

}

int cmp2(const void *a, const void *b)

{

    if( (*(Line *)a).x > (*(Line *)b).x ) return 1;

    else return -1;

}

void insertLength(int pos)

{

    if(pt[pos].flag > 0)

        pt[pos].len = (pt[pos].r - pt[pos].l);

    else if( (pt[pos].left+1) == pt[pos].right )

        pt[pos].len = 0;

    else

        pt[pos].len = (pt[pos*2].len + pt[pos*2+1].len);

}

void buildTree(int l, int r, int pos)

{

    pt[pos].left = l, pt[pos].right = r;

    pt[pos].l = y[l], pt[pos].r = y[r];

    pt[pos].len = pt[pos].flag = 0;

   

    if( (l+1) == r ) return ;

    int mid = (l+r)/2;

    buildTree(l, mid, pos*2);

    buildTree(mid, r, pos*2+1);

}

void insert(Line e, int pos)

{

    if(pt[pos].l == e.y1 && pt[pos].r == e.y2)

    {

        pt[pos].flag += e.flag;

        insertLength(pos);

        return ;

    }

   

    if(e.y1 >= pt[pos*2+1].l)

        insert(e, pos*2+1);

    else if(e.y2 <= pt[pos*2].r)

        insert(e, pos*2);

    else

    {

        Line temp = e;

        temp.y2 = pt[pos*2].r;

        insert(temp, pos*2);

       

        temp = e;

        temp.y1 = pt[pos*2+1].l;

        insert(temp, pos*2+1);

    }

    insertLength(pos);

}

int main()

{

    int caseNum = 1;

    freopen("input.txt","r",stdin);

    while(scanf("%d",&n) != EOF && n != 0)

    {

        num = 1;

        for(int i = 1; i <= n; ++i)

        {

            scanf("%lf %lf %lf %lf",&x1, &y1, &x2, &y2);

            line[num].x = x1;

            line[num].y1 = y1;

            line[num].y2 = y2;

         
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: