平面最近点对模板
导论书上几何部分有讲 二分递归算法 将p数组分为左右两部分 分别递归求出两部分的最小距离 然后再求出左右之间的最小距离 取其最小。
因为左右的最小距离minz已经求出,在求它俩部分之间的最小距离时就有了限制,fabs(x1-x2)<minz fabs(y1-y2)<minz 可以证明出需要用到的点是小于等于8个的,导论上有证。
开两个按x排序的数组和按y排序的数组加快分块的速度;
复杂度n*log(n)
几篇讲的不错的博客
讲解很清晰http://blog.csdn.net/zmlcool/article/details/6727377
有图更好理解 http://blog.csdn.net/guyulongcs/article/details/6841550
struct point { double x,y; int id; //int flag; point(double x=0,double y =0):x(x),y(y) {} } p[N],pp[N],py[N]; typedef point pointt; pointt operator -(point a,point b) { return point(a.x-b.x,a.y-b.y); } double dis(point a,point b) { // if(a.flag==b.flag) return INF;//适于两种不同点集间的最小距离 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } bool cmp(point a,point b) { return a.x<b.x; } bool cmpp(point a,point b) { return a.y<b.y; } void binmerge(point py[],point pp[],int l,int m,int r) { int i,j,g=l; for(i = l,j = m+1 ; i <= m&&j <= r ;)//将pp中两块有序的集合 合并为一个有序的集合 if(pp[i].y<pp[j].y) py[g++] = pp[i++]; else py[g++] = pp[j++]; while(i<=m) py[g++] = pp[i++]; while(j<=r) py[g++] = pp[j++]; //memcpy(pp + l, py + l, (r - l + 1) *sizeof(py[0])); } double binshortest(point p[],point pp[],point py[],int l,int r) { if(r-l==1) return dis(p[l],p[r]); if(r-l==2) return min(min(dis(p[l],p[r]),dis(p[l],p[l+1])),dis(p[l+1],p[r])); int mid = (l+r)>>1; int i,j,g = l,o = mid+1; for(i = l ; i <= r ; i++) { if(py[i].id<=mid)//按y坐标顺序将点划分到pp左半数组 pp[g++] = py[i]; else pp[o++] = py[i];//pp右半数组 } double minz = min(binshortest(p,py,pp,l,mid),binshortest(p,py,pp,mid+1,r)); binmerge(py,pp,l,mid,r); g = l; for(i = l ; i <= r ; i++)//找到x坐标满足到中心线距离小于minz的点 if(fabs(py[i].x-py[mid].x)<minz) pp[g++] = py[i]; for(i = l ; i < g ; i++) { for(j = i+1 ; j < g && fabs(pp[i].y-pp[j].y)<minz; j++)//加上y坐标上的限制 minz = min(dis(pp[i],pp[j]),minz); } return minz; } void solve(point p[]) { sort(p,p+n,cmp); for(int i = 0; i < n ; i++) p[i].id = i; memcpy(py,p,n*sizeof(py[0])); sort(p,p+n,cmpp); double ans = binshortest(p,pp,py,0,n-1); }
例题