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

 这道题快把我折磨死了……

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

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

要点:

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); } }

你可能感兴趣的:(struct,tree,search,insert,Build,扩展)