平面最近点对问题正如其名,给定平面上的$n$个点,找出其中的一对点,使得这对点的距离在所有点对中最小。
首先显而易见地我们可以得到这个问题的$O(n^2)$算法,枚举所有点对即可。但是很显然我们可以注意到,这里面有很多点对显然不是最优的,那么我们可以想到一种剪枝方法,就是将只对x坐标差值小于当前已知最小值的点对进行判断(否则必然不是最优解),从而减少判断量。
我们考虑使用分治来实现这种剪枝,先将平面上的点分为两部分,分治求出两部分内部的最近点对距离。之后我们要做的就是枚举两个集合之间的点对,并与两部分内部的最近点对距离比较来得到最近点对距离。这里我们是不需要枚举所有点对的,因为我们已经得到了一个两部分各自内部最小的点对距离,因而我们可以结合上面的根据$x$坐标的剪枝方法,只枚举分别属于两部分的$x$坐标差小于已知最小距离的点对。
这样做的复杂度近似于$O(n\log^2n)$,至于怎么得到的……我也不知道。_(:зゝ∠)_
例题:
1. Vijos 1012 清帝之惑之雍正
2. 平面最近点对(加强版)
链接:https://www.luogu.org/problem/show?pid=1429
另外附上模板:
注意,本模板保留六位小数,不能直接用于提交上面的例题,若要提交请修改输出精度。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 #include <sstream> 7 #include <cctype> 8 #include <cmath> 9 #include <algorithm> 10 #define THE_BEST_PONY "Rainbow Dash" 11 12 using namespace std; 13 const int maxn=220000,INF=~0u>>1; 14 15 struct Point{ 16 double x,y; 17 bool operator < (const Point &a) const { 18 if(x<a.x) return true; 19 if(x>a.x) return false; 20 return y<a.y; 21 } 22 }p[maxn]; 23 24 int n; 25 double ans; 26 27 double DisP(int a,int b){ 28 return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y)); 29 } 30 31 double GetAns(int l,int r){ 32 int mid=(l+r)>>1; 33 if(l==r) return INF; 34 if(l==r-1) return DisP(l,r); 35 double len=min(GetAns(l,mid),GetAns(mid+1,r)); 36 for(int i=mid;i>=l;i--){ 37 if(p[i].x+len<p[mid].x) break; 38 for(int j=mid+1;j<=r;j++){ 39 if(p[mid].x+len<p[j].x) break; 40 len=min(len,DisP(i,j)); 41 } 42 } 43 return len; 44 } 45 46 int main(){ 47 scanf("%d",&n); 48 for(int i=0;i<n;i++) 49 scanf("%lf%lf",&p[i].x,&p[i].y); 50 sort(p,p+n); 51 ans=GetAns(0,n-1); 52 printf("%.6lf",ans); 53 return 0; 54 }