好几天都没写博客了。。
这几天一直都在看关于矩形面积的并,交和周长,到现在也稍微理解了一点。。
用的都是线段树+扫描线,,
共同点都是需要用到离散化,对y坐标进行从小到大排序,除去相等的y点,根据y轴进行建树。。
然后扫描线需要记录是矩形的左边界还是右边界。。。
每一个结点的c表示该线段被覆盖的次数,具体看代码:
矩形面积的并:
# include<stdio.h> # include<stdlib.h> # define N 210 struct node{ double x,y1,y2; int f; }Line[N]; struct node1{ double lf,rf,cnt; int l,r,c; }tree[N*3]; double y[N]; int cmp(const void *a,const void *b) { struct node * c = (struct node *)a; struct node * d = (struct node *)b; return c->x > d->x ?1:-1; } int cmp1(const void *a,const void *b) { return *(double *)a > *(double *)b ? 1:-1; } void bulid(int t,int l,int r) { int mid; tree[t].c=0;tree[t].cnt=0; tree[t].l=l; tree[t].r=r; tree[t].lf=y[l]; tree[t].rf=y[r]; if(l+1==r) return; mid=(l+r)/2; bulid(2*t,l,mid); bulid(2*t+1,mid,r); } void calen(int t) { if(tree[t].c>0) { tree[t].cnt=tree[t].rf-tree[t].lf; return ; } if(tree[t].l+1==tree[t].r) tree[t].cnt=0; else tree[t].cnt=tree[2*t].cnt+tree[2*t+1].cnt; } void updata(int t,node e) { if(e.y1 == tree[t].lf && e.y2==tree[t].rf) { tree[t].c+=e.f; calen(t); return; } if(e.y2 <=tree[2*t].rf ) updata(2*t,e); else if(e.y1 >=tree[2*t+1].lf) updata(2*t+1,e); else { node tmp=e; tmp.y2=tree[2*t].rf; updata(2*t,tmp); tmp=e; tmp.y1=tree[2*t+1].lf; updata(2*t+1,tmp); } calen(t); } int main() { int i,n,ncase=0,t; double x1,y1,x2,y2,ans; while(scanf("%d",&n)!=EOF && n) { ncase++; t=1; for(i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); Line[t].x=x1; Line[t].y1=y1; Line[t].y2=y2; Line[t].f=1; y[t]=y1; Line[t+1].x=x2; Line[t+1].y1=y1; Line[t+1].y2=y2; Line[t+1].f=-1; y[t+1]=y2; t+=2; } qsort(Line+1,t-1,sizeof(Line[1]),cmp); qsort(y+1,t-1,sizeof(y[1]),cmp1); bulid(1,1,t-1); ans=0; for(i=1;i<t;i++) { ans+=tree[1].cnt*(Line[i].x - Line[i-1].x); updata(1,Line[i]); } printf("Test case #%d\nTotal explored area: %.2lf\n\n",ncase,ans); } return 0; }
再介绍一种求矩形面积并的方法,离散。。
对x,y分别离散,最后转化为(2n-1)*(2n-1)个小矩形(n表示矩形的个数),,对于n比较小的情况,这样也可以求矩形的交,甚至矩形的并。。
大致意思就是,每询问一个矩形,记录其左下角的顶点坐标和右上角的顶点坐标,用二分或者hash找出 所对应的下表,然后把这两个顶点之间所有的小矩形
都标记一下,最后再遍历一遍,对于被标记的小矩形,sum加上其面积,ok。。
代码:
# include<stdio.h> # include<string.h> # include<stdlib.h> struct node{ double x1,y1,x2,y2; }s[105]; double y[205],x[205]; int visit[205][205],xs,ys; int cmp(const void *a, const void *b) { return *(double *)a > *(double *)b ? 1 : -1; } int find1(double ch) {//一定会找到 int left,right,mid; left=1; right=xs; while(right>=left) { mid=(right+left)/2; if(x[mid]==ch) return mid; else if(x[mid]>ch) right=mid-1; else left=mid+1; } } int find2(double ch) {//一定会找到 int left,right,mid; left=1; right=ys; while(right>=left) { mid=(right+left)/2; if(y[mid]==ch) return mid; else if(y[mid]>ch) right=mid-1; else left=mid+1; } } int main() { int i,j,k,x11,y11,x22,y22,ncase=0,t,n; double ans; while(scanf("%d",&n)!=EOF) { ncase++; if(n==0) break; t=0; for(i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&s[i].x1,&s[i].y1,&s[i].x2,&s[i].y2); t++; y[t]=s[i].y1; x[t]=s[i].x1; t++; y[t]=s[i].y2; x[t]=s[i].x2; } qsort(y+1,t,sizeof(y[1]),cmp); qsort(x+1,t,sizeof(x[1]),cmp); ys=1; for(i=2;i<=t;i++) if(y[i]!=y[i-1]) {ys++;y[ys]=y[i];} xs=1; for(i=2;i<=t;i++) if(x[i]!=x[i-1]) {xs++;x[xs]=x[i];} memset(visit,0,sizeof(visit)); for(i=1;i<=n;i++) { x11=find1(s[i].x1); x22=find1(s[i].x2); y11=find2(s[i].y1); y22=find2(s[i].y2); for(j=x11;j<x22;j++) for(k=y11;k<y22;k++) visit[j][k]=1; } ans=0; for(i=1;i<xs;i++) for(j=1;j<ys;j++) if(visit[i][j]==1) { ans+=(x[i+1]-x[i])*(y[j+1]-y[j]); } printf("Test case #%d\n",ncase); printf("Total explored area: %.2lf\n\n",ans); } return 0; }
矩形面积的交:
//这个比矩形面积的并多了一个incalen函数,因为只有该线段被覆盖的至少两次 所围成的面积才是相交的。,,
# include<stdio.h> # include<stdlib.h> # define N 1010 struct node1{ double x,y1,y2; int f; }line[N*2]; struct node2{ double rf,lf,cnt,incnt; int l,r,c; }tree[N*5]; double y[N*2]; int cmp1(const void *a,const void *b) { struct node1 *c=(struct node1*)a; struct node1 *d=(struct node1*)b; if(c->x == d->x) return c->f - d->f; return c->x > d->x ? 1 : -1; } int cmp2(const void *a,const void *b) { return *(double *)a > *(double *)b ? 1 : -1; } void bulid(int t,int l,int r) { int mid; tree[t].c=0; tree[t].cnt=0; tree[t].incnt=0; tree[t].l=l; tree[t].r=r; tree[t].lf=y[l]; tree[t].rf=y[r]; if(l+1==r) return; mid=(l+r)/2; bulid(2*t,l,mid); bulid(2*t+1,mid,r); } void calen(int t) { if(tree[t].c>0) { tree[t].cnt=tree[t].rf-tree[t].lf; return ; } if(tree[t].l+1==tree[t].r) tree[t].cnt=0; else tree[t].cnt=tree[2*t].cnt+tree[2*t+1].cnt; } void incalen(int t) { if(tree[t].c>=2) { tree[t].incnt=tree[t].rf-tree[t].lf; return; } if(tree[t].l+1==tree[t].r) tree[t].incnt=0; else if(tree[t].c==1) { tree[t].incnt=tree[2*t].cnt+tree[2*t+1].cnt; } else tree[t].incnt=tree[2*t].incnt+tree[2*t+1].incnt; } void updata(int t,node1 e) { node1 tmp; if(e.y1==tree[t].lf && e.y2==tree[t].rf) { tree[t].c+=e.f; calen(t); incalen(t); return; } if(e.y2<=tree[2*t].rf) updata(2*t,e); else if(e.y1>=tree[2*t+1].lf) updata(2*t+1,e); else { tmp=e; tmp.y2=tree[2*t].rf; updata(2*t,tmp); tmp=e; tmp.y1=tree[2*t+1].lf; updata(2*t+1,tmp); } calen(t); incalen(t); } int main() { int i,ncase,n,t; double x1,x2,y1,y2,ans; scanf("%d",&ncase); while(ncase--) { scanf("%d",&n); t=1; for(i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[t].x=x1; line[t].y1=y1; line[t].y2=y2; line[t].f=1; y[t]=y1; line[t+1].x=x2; line[t+1].y1=y1; line[t+1].y2=y2; line[t+1].f=-1; y[t+1]=y2; t+=2; } qsort(line+1,t-1,sizeof(line[1]),cmp1); qsort(y+1,t-1,sizeof(y[1]),cmp2); bulid(1,1,t-1); ans=0; for(i=1;i<t;i++) { ans+=tree[1].incnt*(line[i].x - line[i-1].x); updata(1,line[i]); } printf("%.2lf\n",ans); } return 0; }
矩形的周长:
//我感觉这个不太好理解,,和矩形面积的并有点的相似,不过需要在结点里面加好多的域进行判断。。
# include<stdio.h> # include<stdlib.h> # include<math.h> # define N 5005 struct node1{ int x,y1,y2; int f; }line[2*N]; struct node2{ int l,r; int lf,rf;/*左右区间所对应的y值*/ int cnt;/*节点上线段的测度*/ int count;/*节点被线段完全覆盖的次数*/ int lines;/*节点上所包含的线段的段数*/ int lb,rb;/*节点的左右端点是否被覆盖*/ }tree[4*N]; int y[2*N]; int cmp1(const void *a,const void *b) { struct node1*c=(struct node1 *)a; struct node1*d=(struct node1 *)b; if(c->x!=d->x) return c->x - d->x; return d->f - c->f;/*先入 再出*/ } int cmp2(const void *a,const void *b) { return *(int *)a - *(int *)b; } void bulid(int t,int l,int r) { int mid; tree[t].lines=0; tree[t].cnt=0; tree[t].count=0; tree[t].lb=tree[t].rb=0; tree[t].l=l; tree[t].r=r; tree[t].lf=y[l]; tree[t].rf=y[r]; if(l+1==r) return; mid=(l+r)/2; bulid(2*t,l,mid); bulid(2*t+1,mid,r); } void calen(int t) { if(tree[t].count>0) { tree[t].cnt=tree[t].rf-tree[t].lf; tree[t].lines=1; return; } if(tree[t].l+1==tree[t].r) { tree[t].cnt=0; tree[t].lines=0; } else { tree[t].cnt=tree[2*t].cnt+tree[2*t+1].cnt; tree[t].lines=tree[2*t].lines+tree[2*t+1].lines; if(tree[2*t].rb!=0&&tree[2*t+1].lb!=0) tree[t].lines--; } } void updata(int t,node1 e) { node1 tmp; if(tree[t].lf==e.y1) tree[t].lb+=e.f; if(tree[t].rf==e.y2) tree[t].rb+=e.f; if(tree[t].lf==e.y1 && tree[t].rf==e.y2) tree[t].count+=e.f; else if(e.y2<=tree[2*t].rf) updata(2*t,e); else if(e.y1>=tree[2*t+1].lf) updata(2*t+1,e); else { tmp=e; tmp.y2=tree[2*t].rf; updata(2*t,tmp); tmp=e; tmp.y1=tree[2*t+1].lf; updata(2*t+1,tmp); } calen(t); } int main() { int i,n,ys,ans,x1,x2,y1,y2,lastlen,t,lines; while(scanf("%d",&n),n) { t=1; for(i=1;i<=n;i++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); line[t].x=x1; line[t].y1=y1; line[t].y2=y2; line[t].f=1; y[t]=y1; line[t+1].x=x2; line[t+1].y1=y1; line[t+1].y2=y2; line[t+1].f=-1; y[t+1]=y2; t+=2; } qsort(line+1,t-1,sizeof(line[1]),cmp1); qsort(y+1,t-1,sizeof(y[1]),cmp2); ys=2; for(i=2;i<t;i++) { if(y[i]!=y[i-1]) {y[ys]=y[i];ys++;} }/*去除y坐标相同的*/ bulid(1,1,ys-1); ans=0; lastlen=0; lines=0; for(i=1;i<t;i++) { updata(1,line[i]); if(i!=1) ans+=lines*(line[i].x - line[i-1].x)*2; ans+=abs(lastlen - tree[1].cnt); lastlen=tree[1].cnt; lines=tree[1].lines; } printf("%d\n",ans); } return 0; }
现在有的还想的不是很明白,,以后有时间了再回顾下。。。