平面最近点对,即平面中距离最近的两点
分治算法:
int SOLVE(int left,int right)//求解点集中区间[left,right]中的最近点对
{
double ans; //answer
0) 调用前的预处理:对所有点排序,以x为第一关键词y为第二关键字 , 从小到大;
1) 将所有点按x坐标分成左右两部分;
/* 分析当前集合[left,right]中的最近点对,有两种可能:
1. 当前集合中的最近点对,点对的两点同属于集合[left,mid]或同属于集合[mid,right]
则ans = min(集合1中所有点的最近距离, 集合2中所有点的最近距离)
2. 当前集合最近点对中的两点分属于不同集合:[left,mid]和[mid,right]
则需要对两个集合进行合并,找出是否存在p∈[left,mid],q∈[mid,right],使得distance(p,q)小于当前ans(即步骤1中求得的ans);
*/
2) Mid = (left+right)/2;
ans = min( SOLVE(left,mid), SOLVE(mid,right) );
即:递归求解左右两部分中的最近距离,并取最小值;
//此步骤实现上文分析中的第一种情况
/*
再次进行分析
我们将集合[left,right]用x = mid这条直线分割成两部分
则如果画出直线l1:x=mid-ans 和 l2:x=mid+ans,显然如果有p∈[left,mid], q∈[mid,right]且distance(p,q) < ans则p,q一定在直线l1和直线l2之间,否则distance(p,q)必定大于ans。
于是扫描出在l1和l2之间的点
*/
3) 建立缓存数组temp[];
for i = left TO right
{
如果 abs(Point[i].x - Point[mid].x) <= ans
则向temp中加入点Point[i];
}
/*
对于temp中的点,枚举求所有点中距离最近两点的距离,然后与ans比较即可。
枚举的时候不必两两枚举。观察下图中的点p
*/
4) sort(temp);
for i = 0 TO k-1
{
for j = i+1 TO k-1
如果 temp[j].y - temp[i].y >= ans break;
ans = min( ans, distance(temp[i], temp[j]) );
}
5) return ans;
}
算法的时间复杂度
由鸽巢原理,代码中第四步的枚举实际上最多只会枚举6个点,效率极高(一种蒟蒻的证明请看下方的评论)
本算法时间复杂度为O(n log n)
代码:
#include <stdio.h> #include <stdlib.h> #include <math.h> #define MIN( x , y ) ( (x) < (y) ? (x):(y) ) struct _Point { long long x; long long y; }Points[100000] , Tmp[1000]; int cmpxy ( const void *pa , const void *pb ) { struct _Point *a = (struct _Point *)pa; struct _Point *b = (struct _Point *)pb; if ( a->x == b->x ) return a->y > b->y ? 1:-1; else return a->x > b->x ? 1:-1; } int cmpy ( const void *pa , const void *pb ) { struct _Point *a = (struct _Point *)pa; struct _Point *b = (struct _Point *)pb; return a->y > b->y ? 1:-1; } double dis ( struct _Point a , struct _Point b ) { return sqrt ( (double) ( (a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) ) ); } /*分治法求计算几何中平面点最近两点距离*/ double min_length ( struct _Point *p , long left , long right ) { double min; double d1,d2; long mid; long i , j ,k; if ( left == right ) return -1; if ( left + 1 == right ) return dis ( p[ left ] , p[ right ] ); mid = ( left + right ) >> 1; d1 = min_length ( p , left , mid ); d2 = min_length ( p , mid , right ); min = MIN( d1 , d2 ); for ( k = 0 , i = left ; i <= right ; i++ ) { if ( fabs ( p[i].x - p[mid].x ) <= min ) Tmp[k++] = p[i]; } qsort ( Tmp , k , sizeof ( Tmp[0] ) , cmpy ); for ( i = 0 ; i < k - 1 ; i++ ) { for ( j = i + 1 ; j < k ; j++ ) { if ( fabs( Tmp[i].y - Tmp[j].y ) >= min ) break; min = MIN ( min , dis ( Tmp[i] , Tmp[j] ) ); } } return min; } int main ( int argc , char *argv[] ) { long n; long i; scanf("%ld" , &n ); for ( i = 0 ; i < n ; i++ ) scanf ("%ld%ld" , &Points[i].x , &Points[i].y ); qsort ( Points , n , sizeof ( Points[0] ) , cmpxy ); printf ("%.3f" , min_length ( Points , 0 , n - 1 ) ); return 0; }
部分转载:http://blog.csdn.net/lytning/article/details/25370169