您的位置:首页 > 其它

poj 1151 线段树+扫描线(Atlantis矩形面积)

2015-08-25 16:09 169 查看
题意:给定n个矩形 (n<= 100),其顶点坐标是浮点数,可能互相重叠,问这些矩形覆盖到的面积是多大。

思路:线段树+扫描线。用一条直线从左到右扫描,碰到一条矩形竖边的时候,就计算该直线有多长被矩形覆盖,以及被覆盖部分是覆盖了几重。碰到矩形左边,要增加被覆盖的长度,碰到右边, 要减少被覆盖的长度。每碰到一条矩形的纵边,覆盖面积就增加 Len * 该纵边到下一条纵边的距离。Len是此时扫描线被矩形覆盖的长度。

在Y轴进行离散化。n个矩形的2n个横边纵 坐标共构成最多2n-1个区间的边界,对这些区间编号,建立起线段树。

注意灵活运用stl提供的函数。

unique的作用是从输入序列中“删除”所有相邻的重复元素,即去重。

lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置,在本题中即返回查找到的y数组下标。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 100005
struct line{                //表示矩形的竖边
double x,y1,y2;
int l;                  //表示是否为矩形的左边
bool operator<(const line &b) const{
return x<b.x;
}
}line[N<<1];
struct tree{
int l,r;
int num;                //覆盖的重数
double len;
}t[N<<3];
double y[N<<1];
int ylen;
int T = 1,n;
int mid(int a,int b){
return (a+b)>>1;
}
void build(int r,int left,int right){
t[r].l = left;
t[r].r = right;
t[r].num = t[r].len = 0;
if(left == right)
return;
build(r*2, left, mid(left, right));
build(r*2+1, mid(left, right)+1, right);
}
void insert(int r,int left,int right){
int mm = mid(t[r].l,t[r].r);
if(t[r].l == left && t[r].r == right){
t[r].len = y[right]-y[left-1];
t[r].num++;
return;
}
if(right<=mm)
insert(r*2, left, right);
else if(left>mm)
insert(r*2+1, left, right);
else{
insert(r*2, left, mm);
insert(r*2+1, mm+1, right);
}
if(t[r].num == 0)
t[r].len = t[r*2].len + t[r*2+1].len;
}
void del(int r,int left,int right){
int mm = mid(t[r].l, t[r].r);
if(t[r].l == left && t[r].r == right){
t[r].num--;
if(t[r].num == 0){
if(left == right)
t[r].len = 0;
else
t[r].len = t[r*2].len+t[r*2+1].len;
}
return;
}
if(right <= mm)
del(r*2,left,right);
else if(left > mm)
del(r*2+1,left,right);
else{
del(r*2,left,mm);
del(r*2+1,mm+1,right);
}
if(t[r].num == 0)
t[r].len = t[r*2].len+t[r*2+1].len;
}
int main(){
while(scanf("%d",&n) && n){
int i,j,y1,y2;
double a,b,c,d,res=0;
ylen = 0;
for(i = 0;i<n;i++){
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
y[ylen++] = b;
y[ylen++] = d;
line[i*2].x = a;
line[i*2].l = 1;
line[i*2+1].l = 0;
line[i*2+1].x = c;
line[i*2].y1 = line[i*2+1].y1 = b;
line[i*2].y2 = line[i*2+1].y2 = d;
}
sort(line,line+2*n);
sort(y,y+ylen);
ylen = unique(y, y+ylen)-y;
build(1,1,ylen-1);          //按照区间的个数建树
for(i = 0;i<2*n-1;i++){
y1 = lower_bound(y,y+ylen,line[i].y1)-y;
y2 = lower_bound(y,y+ylen,line[i].y2)-y;
if(line[i].l)           //注意,y[0]~y[1]是区间1
insert(1,y1+1,y2);
else
del(1,y1+1,y2);
res += t[1].len*(line[i+1].x - line[i].x);
}
printf("Test case #%d\n",T++);
printf("Total explored area: %.2lf\n\n",res);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: