平面最近點距離問題(分治法)
算法:
0:把所有的點按照橫坐標排序
1:用一條豎直的線L將所有的點分成兩等份
2:遞歸算出左半部分的最近兩點距離d1,右半部分的最近兩點距離d2,取d=min(d1,d2)
3:算出“一個在左半部分,另一個在右半部分”這樣的點對的最短距離d3。
4:結果=min(d1,d2,d3)
關鍵就是這第3步。貌似這需要n^2的時間,把左邊每個點和右邊每個點都對比一下。其實不然。秘密就在這裏。 首先,兩邊的點,與分割線L的距離超過d的,都可以扔掉了。 其次,即使兩個點P1,P2(不妨令P1在左邊,P2在右邊)與分割線L的距離(水平距離)都小於d,如果它們的縱坐標之差大於d,也沒戲。 就是這兩點使得搜索範圍大大減小: 對於左半部分的,與L的距離在d之內的,每個P1來說:右半部分內,符合以上兩個條件的點P2最多只有6個! 原因就是: d是兩個半平面各自內,任意兩點的最小距離,因此在同一個半平面內,任何兩點距離都不可能超過d。 我們又要求P1和P2的水平距離不能超過d,垂直距離也不能超過d,在這個d*2d的小方塊內,最多只能放下6個距離不小於d的點。 因此,第3步總的比較距離的次數不超過n*6。
第3步的具體做法是:
3.1 刪除所有到L的距離大於d的點。 O(n)
3.2 把右半平面的點按照縱坐標y排序。 O(nlogn)
3.3 對於左半平面內的每個點P1,找出右半平面內縱坐標與P1的縱坐標的差在d以內的點P2,計算距離取最小值,算出d3。 O(n*6) = O(n) 因為3.2的排序需要O(nlogn), 所以整個算法的復雜度就是O(n((logn)^2))。
/** 最近點對問題,時間復雜度為O(n*logn*logn) */ #include <iostream> #include <cstdio> #include<cstring> #include <cmath> #include <algorithm> using namespace std; const double INF = 1e20; const int N = 100005; struct Point { double x; double y; }point[N]; int n; int tmpt[N]; bool cmpxy(const Point& a, const Point& b) { if(a.x != b.x)return a.x < b.x; return a.y < b.y; } bool cmpy(const int& a, const int& b) { return point[a].y < point[b].y; } double min(double a, double b) { return a < b ? a : b; } double dis(int i, int j) { return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x) + (point[i].y-point[j].y)*(point[i].y-point[j].y)); } double Closest_Pair(int left, int right) { double d = INF; if(left==right) return d; if(left + 1 == right) return dis(left, right); int mid = (left+right)>>1; double d1 = Closest_Pair(left,mid); double d2 = Closest_Pair(mid+1,right); d = min(d1,d2); int i,j,k=0; //分離出寬度為d的區間 保存到tmpt中 for(i = left; i <= right; i++) { if(fabs(point[mid].x-point[i].x) <= d) tmpt[k++] = i; } sort(tmpt,tmpt+k,cmpy); //暴力搜索 tmpt中的最短距離 for(i = 0; i < k; i++) { for(j = i+1; j < k && point[tmpt[j]].y-point[tmpt[i]].y<d; j++) { double d3 = dis(tmpt[i],tmpt[j]); if(d > d3) d = d3; } } return d; } int main() { while(true) { scanf("%d",&n); if(n==0) break; for(int i = 0; i < n; i++) scanf("%lf %lf",&point[i].x,&point[i].y); sort(point,point+n,cmpxy); printf("%.2lf\n",Closest_Pair(0,n-1)); } return 0; }
平面最近點距離問題(分治法)