裸的扫描线,学习扫描线的题目。具体扫描线的原理我不讲了,我是看大神们的博客懂得,就算写也没大神屌。下面我给出我的代码,里面的注释是我认为比较重要的地方
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int MAX=210;
int N;
double y[MAX];
double sum=0;
typedef struct{
double x,y1,y2;
int f;
void set(double xx,double yy1,double yy2,int ff)
{
x=xx;
y1=yy1;
y2=yy2;
f=ff;
}
}Node;
Node no[MAX];
typedef struct
{
int left,right;
int cover;
double realleft,realright,len;
void set(int l,int r)
{
left=l;
right=r;
cover=0;
len=0;
realleft=y[l];
realright=y[r];
}
}P;
P per[4*MAX];
bool cmp(const Node &n1,const Node &n2)
{
return n1.x<n2.x;
}
void build(int id,int left,int right)
{
per[id].set(left,right);
if(right-left==1)
return;
int mid=(left+right)/2;
build(id<<1,left,mid);
build(id<<1|1,mid,right); //我写线段树的习惯是写mid+1,但此处不能是mid+1,因为如果写mid+1,那y[mid]与y[mid+1]间的区间就被漏掉;搜索不到这个区间,大家/可以好好想想
}
void get_len(int t)
{
//cover>0,这段区间被全部覆盖,那么长度当然是右边长度减左边长度
if(per[t].cover>0)
per[t].len=per[t].realright-per[t].realleft;
else
//叶子节点当然是0
if(per[t].left+1==per[t].right)
per[t].len=0;
else
//该处的意思是,比如该区间有三段小区间,第一第三个小区间都被覆盖,但第二个没有,所以这时候不能右边减左边
per[t].len=per[t<<1].len+per[t<<1|1].len;
}
void update(int id,Node node)
{
if(per[id].realleft==node.y1&&per[id].realright==node.y2)
{
per[id].cover+=node.f;
get_len(id);
return;
}
if(per[id<<1].realright>=node.y2)
update(id<<1,node);
else
if(per[id<<1|1].realleft<=node.y1)
update(id<<1|1,node);
else
{
Node m=node;
m.y2=per[id<<1].realright;
update(id<<1,m);
m=node;
m.y1=per[id<<1|1].realleft;
update(id<<1|1,m);
}
get_len(id);
}
int main()
{
double x1,x2,y1,y2;
int num=1;
while(scanf("%d",&N)!=EOF&&N!=0)
{
sum=0;
int t=0;
for(int i=0;i<N;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
no[t].set(x1,y1,y2,1);
y[t]=y1;
t++;
no[t].set(x2,y1,y2,-1);
y[t]=y2;
t++;
}
sort(no,no+t,cmp);
sort(y,y+t);
build(1,0,t-1);
printf("Test case #%d\n",num++);
update(1,no[0]);
for(int i=1;i<t;i++)
{
sum+=per[1].len*(no[i].x-no[i-1].x);
update(1,no[i]);
}
printf("Total explored area: %.2lf\n\n",sum);
}
return 0;
}