POJ1151 HDU1542 求矩形面积并 线段树离散化+扫描线

Atlantis

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

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

扫描二维码关注公众号,回复: 2965575 查看本文章

Source

Mid-Central European Regional Contest 2000

题意:

给出 N 个矩形,求面积并。(N ≤ 100) 每个矩形给出左下角和右上角坐标。

离散化

当要建树的数据范围很大,数量相对较小,例如 [ 1 , 1e9 ]。 此时如果数组要开到 (1e9 << 2) 显然是不可能的。 因此,需要离散化。 离散化,就是把无限空间中有限的个体映射到有限的空间中去, 以此提高算法的时空效率。 通俗的说,离散化是在不改变数据相对大小的条件下,对数据 进行相应的缩小。 

 例如一组数据 : 1,10,100,1000,100000,1000000000

离散化后可变为:1,2,3,4,5,6

在使用算法时,我们用小的数据代替大的数据。

[1,10] � [1,2], [1,100] � [1,3] ,[100,1000000000] � [3,6]

扫描线法:

假设有条扫描线,从左往右(从右往左),或者从上而下(从下而上)扫描过整个多边形(即矩形叠加之后的图形),如果竖直方向上扫描(从上往下,或者从下往上),则离散化横坐标。如果是水平方向扫描(从左往右,或者从右往左),则离散化纵坐标。

下面分析方法是:从下往上扫描,离散化横坐标。

扫描之前还需要做一个工作,保存好矩形的上下边,并且按照他们所处的高度进行排序,如果是上边,就标记为-1,下边就标记为1,用一个结构体来保存上下边

struct segment{
    double l,r;//这条边的左右坐标
    double h;//这条边所处的高度
    int f;//上边标记为-1,下边标记为1
};

扫描线从下往上扫描,每遇到一条上下边就停下来,将这条线段投影到总区间(即所有矩形的横跨长度),这个投影对应的其实是插入和删除线段操作;如果扫描到下边,就相当于在总区间中插入一条线段(即投影到总区间,对应的那一段的值都要增1);如果扫描到上边,就相当于在总区间中删除一条线段(即投影到总区间,对应的那一段的值都要减1);

如果总区间某一段的值为0,说明没有线段覆盖到;如果总区间某一段的值为正,则说明有线段覆盖到;不可能为负值

每扫描到一条上下边并投影到总区间后,就判断总区间现在被覆盖的总长度,然后用下一条边的高度减去当前这条边的长度,乘上总区间被覆盖的长度,就能得到一块面积,并依次做下去,就能得到总面积

#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 210//最多有100个矩形,200条上下边
struct Node{
    int l,r;//线段的左右整点
    int c;//用来记录重叠情况
    double cnt;//用来计算实际长度
    double lf,rf;//对应左右浮点数端点
}segTree[maxn<<2];
struct Line
{
    double x;//线段的x坐标
    double y1,y2;//线段对应的上端点和下端点
    int f;//上边标记为-1,下边标记为1
}line[maxn];

bool cmp(Line a,Line b)
{
    return a.x<b.x;
}
double y[maxn];//记录y坐标的数组
void Build(int t,int l,int r)//构造线段树
{
    segTree[t].l=l;
    segTree[t].r=r;
    segTree[t].cnt=segTree[t].c=0;
    segTree[t].lf=y[l];
    segTree[t].rf=y[r];
    if(l+1==r)  return ;
    int mid=(l+r)>>1;
    //递归构造
    Build(t<<1,l,mid);
    Build(t<<1|1,mid,r);
}
void calen(int t)//计算长度
{
    if(segTree[t].c>0){
        segTree[t].cnt=segTree[t].rf-segTree[t].lf;//线段的实际长度
        return ;
    }
    if(segTree[t].l+1==segTree[t].r)
        segTree[t].cnt=0;
    else
        segTree[t].cnt=segTree[t<<1].cnt+segTree[t<<1|1].cnt;
}
void update(int t,Line e)//加入线段e,后更新线段树
{
    if(e.y1==segTree[t].lf&&e.y2==segTree[t].rf)
    {
        segTree[t].c+=e.f;
        calen(t);
        return;
    }
    if(e.y2<=segTree[t<<1].rf)  update(t<<1,e);
    else if(e.y1>=segTree[t<<1|1].lf)  update(t<<1|1,e);
    else
    {
        Line tmp=e;
        tmp.y2=segTree[t<<1].rf;
        update(t<<1,tmp);
        tmp=e;
        tmp.y1=segTree[t<<1|1].lf;
        update(t<<1|1,tmp);
    }
    calen(t);
}
int main()
{
    int n,Case=1;
    double x1,y1;//矩形的 (X1,Y1) 为该矩形的左下角, (X2,Y2) 为矩形的右上角。
    double x2,y2;
    while(scanf("%d",&n)&&n){
        int t=1;
        for(int 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].f=1;//左边
            y[t]=y1;
            t++;

            line[t].x=x2;
            line[t].y1=y1;
            line[t].y2=y2;
            line[t].f=-1;//右边
            y[t]=y2;
            t++;
        }
        //t为线段个数+1
        sort(line+1,line+t,cmp);//对所有线段排序
        sort(y+1,y+t);//总区间排序
        Build(1,1,t-1);//建树
        update(1,line[1]);
        double res=0;
        for(int i=2;i<t;i++)
        {
            res+=segTree[1].cnt*(line[i].x-line[i-1].x);//计算面积
            update(1,line[i]);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",Case++,res);
        //看来POJ上%.2f可以过,%.2lf却不行
    }
    return 0;
}

离散化需要用到的函数:

1. void sort ( first, last, cmp); 对 [first,last) 的元素进行排序。

2. Interator unique( first, last); 对 [first,last) 的元素进行去重操作,需先排序。

3. Interator lower_bound( first, last, element); 返回 [first,last) 中第一个 element 的地址。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 205;
struct node{
  double l, r, h;
  int c;
  bool operator < (struct node a) const{//按高度进行排序
    return h < a.h;
  }
}edge[maxn];//线段
double q[maxn << 1], sum[maxn << 2];
int tree[maxn << 2], cnt, res,T = 0;

void update(int now, int l, int r){
  if(tree[now] != 0) sum[now] = q[r + 1] - q[l];
  else if(l == r) sum[now] = 0;
  else sum[now] = sum[now << 1] + sum[(now << 1)|1];
}

void build_tree(int L, int R, int c, int l, int r, int now) {
  if (L <= l && R >= r) {
    tree[now] += c;
    update(now, l, r);
    return;
  }
  int mid = l + (r - l)/2;
  if (L <= mid) build_tree(L, R, c, l, mid, now << 1);
  if (R > mid) build_tree(L, R, c, mid + 1, r, (now << 1)|1);
  update(now, l, r);
}

int main()
{
  //freopen("test.in", "r", stdin);
  //freopen("test.out", "w",stdout);
  int n,Case=1;
  double a, b, c, d;
  while (scanf("%d", &n) != EOF && n != 0) {
    cnt = 0;
    memset(tree, 0, sizeof tree);
    memset(sum, 0, sizeof sum);

    for (int i = 0; i < n; ++i) {
      scanf("%lf %lf %lf %lf", &a, &b, &c, &d);
      q[cnt] = a;
      edge[cnt].l = a;
      edge[cnt].r = c;
      edge[cnt].h = b;
      edge[cnt].c = 1;
      cnt++;
      q[cnt] = c;
      edge[cnt].l = a;
      edge[cnt].r = c;
      edge[cnt].h = d;
      edge[cnt].c = -1;
      cnt++;
    }

    sort(edge, edge + cnt);
    sort(q, q + cnt);

    res = 1;
    //去重操作
    res=unique(q,q+cnt)-q;
    /*
    for (int i = 1; i < cnt; ++i) {
      if (q[i] != q[i - 1]) q[res++] = q[i];
    }
    */
    double ans = 0;
    for (int i = 0; i < cnt - 1; ++i) {
      int l = lower_bound(q, q + res, edge[i].l) - q;
      int r = lower_bound(q, q + res, edge[i].r) - q - 1;
      build_tree(l, r, edge[i].c, 0, res - 1, 1);
      ans += sum[1]*(edge[i + 1].h - edge[i].h);
    }
     printf("Test case #%d\nTotal explored area: %.2f\n\n",Case++,ans);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40507857/article/details/81911038