您的位置:首页 > 其它

线段树+离散化+线性扫描 POJ 1151 Atlantis

2009-11-29 21:09 393 查看
这道题快把我折磨死了……

首先不会做,听大牛说这题很简单,用线段树+离散化就可以了,可是我一点思路都没有!

后来看了别人的思路,自己写了个程序算是过了,既然是经典的题,就做一下笔记吧。

要点:

1,离散化的对象:按照之前接触过的线段树,都是存的线段,我想应该可以扩展,实现举行区域的保存(二维线段树?),但是我不会做……后来参考了网上一些讨论,说可以离散化“纵轴”或者“横轴”坐标。

2,加入离散化纵轴,按照一般做法,首先应该排序并删除重复的元素,处理完放在数组my中。这些元素的个数用来作为建立线段树的区域大小。

3,建立线段树,按照2中处理后的纵坐标的范围,建立线段树。

4,线性扫描:既然离散化了纵轴,将每一个矩形的纵轴(有两个)分别插入和删除在线段树中。

对4的解释:在线段树中设置了“测度”和“覆盖数”成员,可以将整个区域划分成“以线段树某节点的测度为长”,以相邻纵轴之间的“距离”(即相应的横坐标差)位宽的许多个不重叠的小的矩形。在扫描每一个纵轴时,按照横坐标从小到达的顺序扫描(从左到右),这样每扫描一个纵轴,就可以计算一个小矩形的面积,然后累加。在整个扫描过程中,线段树的整个“测度”(tree[1].len)就是小矩形的长。要计算每一个矩形的长(即整个线段树的测度),只需要在线段树中和本矩形相关的线段(纵轴),所以,当第一次扫描到一个原始矩形的纵轴时(可以用-1表示),把它插入到线段树,用于更新当前线段树的测度。当第二次(可以用1表示)扫描到一个原始矩形的纵轴时,就必须从线段树删除(之前插入的),因为这时候这个原始矩形的面积已经被计算完毕。

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
const int M=1000;
struct node
{
int l,r;
int cov;
double len;
}tree[M];
struct line
{
double x;
double y1;
double y2;
char f;
}data[M];
double my[M];
int n ;
double sum;
int cmp1(const void *a,const void *b)
{
double * d1=(double*)a;
double *d2 = (double *)b;
if(*d1<*d2)
return -1;
else if(*d1>*d2)
return 1;
else
return 0;
}
int cmp2(const void * a,const void *b)
{
line *l1=(line*)a;
line *l2=(line*)b;
if(l1->x < l2->x)
return -1;
else if(l1->x > l2->x)
return 1;
else
return 0;
}
void build(int l,int r,int root)
{
tree[root].l=l;
tree[root].r=r;
tree[root].cov=0;
tree[root].len=0;
if(l+1==r)
return;
int mid = (tree[root].l + tree[root].r)>>1;
build(l,mid,2*root);
build(mid,r,2*root+1);
}
void del(int l,int r,int p)
{
if(tree[p].l == l && tree[p].r==r)
{
tree[p].cov--;
if(tree[p].cov<=0)
{
if(tree[p].l+1<tree[p].r)
tree[p].len =tree[2*p].len+tree[2*p+1].len;
else
tree[p].len=0;
}
return;
}
else
{
int mid = (tree[p].l + tree[p].r)>>1;
if(r<=mid)
del(l,r,p*2);
else if(l>=mid)
del(l,r,p*2+1);
else
{
del(l,mid,p*2);
del(mid,r,p*2+1);
}
if(tree[p].cov==0)
tree[p].len = tree[p*2].len+tree[p*2+1].len;
}
}
void insert(int l,int r,int p)
{
if(tree[p].l == l && tree[p].r==r)
{
tree[p].cov++;
tree[p].len = my[r]-my[l];
return;
}
else
{
int mid = (tree[p].l + tree[p].r)>>1;
if(r<=mid)
insert(l,r,p*2);
else if(l>=mid)
insert(l,r,p*2+1);
else
{
insert(l,mid,p*2);
insert(mid,r,p*2+1);
}
if(tree[p].cov==0)
tree[p].len = tree[p*2].len+tree[p*2+1].len;
}
//update(p);
}
int bin_search(int n,double key)
{
int l=0;
int h=n-1;
while(l<=h)
{
int  mid = l+(h-l)/2;
if(my[mid] == key)return mid;
else if(my[mid]>key)h=mid-1;
else l=mid+1;
}
return l;
}
int main()
{
scanf("%d",&n);
double x1,y1,x2,y2;
int t=0;
while(n!=0)
{
t++;
sum=0;
memset(tree,0,sizeof(tree));
memset(my,0,sizeof(my));
int l1=0;
int l2=0;
int i = 0;
for(i=0;i<n;i++)
{
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
my[l1++]=y1;
my[l1++]=y2;
data[l2].x=x1;
data[l2].y1=y1;
data[l2].y2=y2;
data[l2].f=-1;
l2++;
data[l2].x=x2;
data[l2].y1=y1;
data[l2].y2=y2;
data[l2].f=1;
l2++;
}
qsort(my,l1,sizeof(double),cmp1);
qsort(data,l2,sizeof(line),cmp2);
int j =1;
for(i=1;i<2*n;i++)//除去重复元素(排序后的y坐标)
{
if(my[i]!=my[i-1])
my[j++]=my[i];
}
l1=j;
build(0,j-1,1);//[0~l1-1]的线段树
for(i=0;i<l2-1;i++)
{
int l,r;
l = bin_search(l1,data[i].y1);
r = bin_search(l1,data[i].y2);
if(data[i].f == -1)//扫描矩形的第一条纵边:插入线段树
insert(l,r,1);
else
del(l,r,1);
sum +=tree[1].len *(data[i+1].x-data[i].x);//计算当前扫描的面积
}
printf("Test case #%d/n",t);
printf("Total explored area: ");
printf("%.2lf/n/n",sum);

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