第一次写扫描线的题,基本是模仿网上的代码,好在把思路理清了。
基本思想就是:把所有竖着的线(横竖都行,不过我更习惯竖着的)存起来,f=1表示是坐标的线段,f=-1表示右边。按x坐标从小到大排序。
按顺序读取线段,用num表示这个线段的个数,wide表示线段的宽度。每次面积+=当前宽度总和wide[1] * 两次x之间的距离。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int N=210;
struct node{
double x,y1,y2;
int f;
}line[N];
int cmp(node a,node b){
return a.x<b.x;
}
double y[N];
double wide[N*4];
int num[N*4];
void build(int l,int r,int rt){
wide[rt]=0;
num[rt]=0;
if(l==r)
return;
int m=(l+r)/2;
build(lson);
build(rson);
}
void push_up(int l,int r,int rt){
if(num[rt]>0)
wide[rt]=y[r+1]-y[l];
else if(l==r)
wide[rt]=0;
else
wide[rt]=wide[rt*2]+wide[rt*2+1];
}
void update(int a,int b,int c,int l,int r,int rt){//线段更新 覆盖
if(a<=l&&b>=r){
num[rt]+=c;
push_up(l,r,rt);
return;
}
int m=(l+r)/2;
if(a<=m)
update(a,b,c,lson);
if(b>m)//a,b表示的是点
update(a,b,c,rson);
push_up(l,r,rt);
}
void addLine(double x,double y1,double y2,int f,int cnt){
line[cnt].x=x;
line[cnt].y1=y1;
line[cnt].y2=y2;
line[cnt].f=f;
}
int main(){
int n,cases=1;
double x1,x2,y1,y2;
while(cin>>n&&n){
int cnt=0;
for(int i=0;i<n;i++){//存储线段
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
addLine(x1,y1,y2,1,cnt);
y[cnt++]=y1;
addLine(x2,y1,y2,-1,cnt);
y[cnt++]=y2;
}
sort(line,line+cnt,cmp);
sort(y,y+cnt);//离散化建树
int nn=unique(y,y+cnt)-y;
build(0,nn-1,1);
double ans=0;
for(int i=0;i<cnt-1;i++){
int left=lower_bound(y,y+nn,line[i].y1)-y;
int right=lower_bound(y,y+nn,line[i].y2)-y-1;//不减是点,减了是线段
update(left,right,line[i].f,0,nn-1,1);
ans+=wide[1]*(line[i+1].x-line[i].x);
}
printf("Test case #%d\n",cases++);
printf("Total explored area: %.2lf\n\n",ans);
}
return 0;
}