题目链接:http://poj.org/problem?id=1151
http://acm.hdu.edu.cn/showproblem.php?pid=1542
题意:给出任意n多矩形,求其总覆盖的面积.
可分别用离散化处理,矩形切割,线段树三种方法实现,以下分别为三种方法的代码:
离散化坐标:
#include<stdio.h> #include<math.h> #include<string.h> #include<stdlib.h> #define M 408 const double eps = 1e-8; typedef struct{ double x1,x2,y1,y2; void set(double x1,double y1,double x2,double y2) {this->x1=x1;this->y1=y1;this->x2=x2;this->y2=y2;} }Rec; Rec r[M]; bool map[M][M]; //数组开太小,RE了一次 double x[M],y[M]; int n,nx,ny; int Cmp(const void *a,const void *b) { return *(double *)a>*(double *)b?1:-1; } int Find(double arr[],int low,int up,double v) {//二分查找,并且一定数组中一定存在与v相等的值 int mid=(low+up)>>1; if(fabs(arr[mid]-v)<eps) return mid; if(arr[mid]>v) return Find(arr,low,mid-1,v); return Find(arr,mid+1,up,v); } int main() { double x1,y1,x2,y2,ans; int t=1,i,j,i1,i2,j1,j2,k; while(scanf("%d",&n),n){ for(nx=ny=0,i=0;i<n;i++){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); r[i].set(x1,y1,x2,y2); x[nx++]=x1;x[nx++]=x2; y[ny++]=y1;y[ny++]=y2; } //排序,进而离散化点 qsort(x,nx,sizeof(x[0]),Cmp); qsort(y,ny,sizeof(y[0]),Cmp); //消除相等的值,进行离散化 for(i=0,j=0;i<nx;i++){ if(fabs(x[i]-x[j])<eps) continue; x[++j]=x[i]; }nx=j; for(i=0,j=0;i<ny;i++){ if(fabs(y[i]-y[j])<eps) continue; y[++j]=y[i]; }ny=j; memset(map,false,sizeof(map)); for(i=0;i<n;i++){ i1=Find(x,0,nx,r[i].x1); i2=Find(x,0,nx,r[i].x2); j1=Find(y,0,ny,r[i].y1); j2=Find(y,0,ny,r[i].y2); for(j=i1;j<i2;j++){//标记覆盖的区域 for(k=j1;k<j2;k++) map[j][k]=true; } } ans=0.0; for(i=0;i<nx;i++){ for(j=0;j<ny;j++){ if(map[i][j]) ans+=(x[i+1]-x[i])*(y[j+1]-y[j]); } } printf("Test case #%d/n",t++); printf("Total explored area: "); printf("%.2f/n/n",ans); } return 0; }
矩形切割:
#include<stdio.h> #include<string.h> #include<stdlib.h> #define M 108 typedef struct talRec{ double x1,y1,x2,y2; talRec *next; }Rec; Rec space[M*24];//手动分配空间 Rec *list; int n; double min(double a,double b) { return a<b?a:b; } double max(double a,double b) { return a>b?a:b; } void Init() {//重置内存空间 int i; for(i=0;i<24*n;i++) space[i].next=&space[i+1]; list=&space[1];list->next=NULL; space[0].next=&space[2]; } void Del(Rec *cur) {//把删掉的结点放入内存分配的链表中 cur->next=space[0].next; space[0].next=cur; } void Add(double x1,double y1,double x2,double y2) {//向链表中插入新矩形,头插法 Rec *cur; //new Rec * cur=space[0].next;//申请新结点 space[0].next=cur->next; cur->x1=x1;cur->y1=y1; cur->x2=x2;cur->y2=y2; cur->next=list->next; list->next=cur; } void Cut(double x1,double y1,double x2,double y2, double x3,double y3,double x4,double y4) {//其中点分别为矩形左下侧点和右上侧点 double k1,k2,k3,k4; if(x2<=x3||y2<=y3||x4<=x1||y4<=y1){ Add(x1,y1,x2,y2);//如果不相交 return ; } k1=max(x1,x3);k2=min(x2,x4); if(x1<k1) Add(x1,y1,k1,y2); if(x2>k2) Add(k2,y1,x2,y2); k3=k1,k4=k2; k1=max(y1,y3);k2=min(y2,y4); if(y1<k1) Add(k3,y1,k4,k1); if(y2>k2) Add(k3,k2,k4,y2); } void New(double x1,double y1,double x2,double y2) {//添加新矩形时,与之前的矩形比较,切割 Rec *cur,*pre,*tmp; pre=list;cur=pre->next; while(cur){ pre->next=cur->next;//删掉当前与之比较的链表中的矩形 if(pre==list){ tmp=cur->next; Cut(cur->x1,cur->y1,cur->x2,cur->y2,x1,y1,x2,y2); while(pre->next!=tmp) pre=pre->next; } else Cut(cur->x1,cur->y1,cur->x2,cur->y2,x1,y1,x2,y2); Del(cur); cur=pre->next; } Add(x1,y1,x2,y2); } int main() { double x1,y1,x2,y2,ans,tmp; int t=1; while(scanf("%d",&n),n){ Init();// while(n--){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); if(x1>x2) tmp=x1,x1=x2,x2=tmp; if(y1>y2) tmp=y1,y1=y2,y2=tmp; New(x1,y1,x2,y2); } for(list=list->next,ans=0.0;list;list=list->next) ans+=(list->x2-list->x1)*(list->y2-list->y1); printf("Test case #%d/n",t++); printf("Total explored area: "); printf("%.2f/n/n",ans); } return 0; }
线段树加扫描线:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> //部分宏定义,左子树,右子树,中间值 #define L(i) (i<<1) #define R(i) (i<<1|1) #define Mid(x,y) ((x+y)>>1) #define M 2010 #define eps 1e-8 typedef struct {//垂线定义,即扫描线 double x,y1,y2; int flag; }Line; typedef struct {//线段树结点定义 int l,r,c; double ln; }SegTree; Line line[M]; SegTree seg[M*3]; double y[M]; int ls,ys; int cmp1(const void *a,const void *b) { Line *p=(Line *)a; Line *q=(Line *)b; if(fabs(p->x-q->x)<eps) return p->flag-q->flag; return p->x>q->x?1:-1; } int cmp2(const void *a,const void *b) { return *(double *)a>*(double *)b?1:-1; } void Create(int i,int l,int r) {//构建一个空的线段树 seg[i].l=l;seg[i].r=r; seg[i].ln=0.0;seg[i].c=0; if(l==r-1) return ; int mid=Mid(l,r); Create(L(i),l,mid); Create(R(i),mid,r); } void Calen(int i) {//更新当前结点段,y轴上覆盖的长度 if(seg[i].c>0){ seg[i].ln=y[seg[i].r]-y[seg[i].l]; return ; } if(seg[i].r-seg[i].l==1) seg[i].ln=0.0; else seg[i].ln=seg[L(i)].ln+seg[R(i)].ln; } void Updata(int i,Line l) {//向线段树增加新的线段 Line tmp; int mid; if(fabs(l.y1-y[seg[i].l])<eps&&fabs(l.y2-y[seg[i].r])<eps){ seg[i].c+=l.flag; Calen(i); return ; } mid=Mid(seg[i].l,seg[i].r); if(fabs(l.y2-y[mid])<eps||l.y2<y[mid]) Updata(L(i),l); else if(fabs(l.y1-y[mid])<eps||l.y1>y[mid]) Updata(R(i),l); else { tmp.flag=l.flag; tmp.y1=l.y1;tmp.y2=y[mid]; Updata(L(i),tmp); tmp.y1=y[mid];tmp.y2=l.y2; Updata(R(i),tmp); } Calen(i); } int main() { int t=1,n; int i,j; double x1,x2,y1,y2; double ans; while(scanf("%d",&n),n){ ls=1; for(i=0;i<n;i++){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[ls].x=x1;line[ls].y1=y1; line[ls].y2=y2;line[ls].flag=1; line[ls+1].x=x2;line[ls+1].y1=y1; line[ls+1].y2=y2;line[ls+1].flag=-1; y[ls++]=y1;y[ls++]=y2; } qsort(line+1,ls-1,sizeof(line[0]),cmp1); qsort(y+1,ls-1,sizeof(y[0]),cmp2); for(i=1,j=1;i<ls;i++){//y轴坐标离散化,此处消去相同的y值 if(fabs(y[j]-y[i])<eps) continue; y[++j]=y[i]; }ys=j; Create(1,1,ls-1); ans=0; for(i=1;i<ls;i++){//求面积 ans+=seg[1].ln*(line[i].x-line[i-1].x); Updata(1,line[i]); } printf("Test case #%d/n",t++); printf("Total explored area: "); printf("%.2f/n/n",ans); } return 0; }
其实,还有好几种做法~~~什么二维线段树,什么方块树~呃,以后再研究~