分治法求平面最近點對
阿新 • • 發佈:2018-11-06
題意
思考
之前考分治的時候有一道題,要用到 \(O(nlogn)\) 求平面最近點對,然而當時我不會……現在寫篇部落格回顧一下。
平面上 \(n\) 個點,讓我們求最近點對,最樸素的想法是列舉,複雜度 \(O(n^2)\)
這樣是顯然過不了 \(1e5\) 的資料的,同時我們也發現對於一個點而言,我們計算了許多無用的情況,如何優化?
分治思路:
首先我們將所有點按橫座標排序,選擇中位點的橫座標為軸,將整個平面分成兩半,那麼答案可以變為:
\(min(\) 左半平面上點對的最近距離,右半平面上點對的最近距離,左半和右半平面各選一個點組成的最短距離 \()\)
重點的優化則在求第三個最近距離上,我們設左右半邊點對距離的最小值為 \(d\)
程式碼
#include<bits/stdc++.h> using namespace std; typedef double D; const int N = 200020; int tmp[N], n; struct node{ D x, y; }P[N]; D sqr(D x){ return x * x; } D calc(int a, int b){ return sqrt( sqr(P[a].x - P[b].x) + sqr(P[a].y - P[b].y) ); } bool cmp(node a, node b){ return a.x == b.x ? a.y < b.y : a.x < b.x; } bool cmp2(int x, int y){ return P[x].y < P[y].y; } D solve(int l, int r){ int cnt = 0; double d = 10000000; if(l == r) return 100000000; if(l + 1 == r) return calc(l, r); int mid = (l + r) >> 1; d = min( solve(l, mid), d ); d = min( solve(mid+1, r), d); for(int i=l; i<=r; i++){ if( fabs(P[mid].x - P[i].x) <= d){ tmp[++cnt] = i; } } sort(tmp+1, tmp+cnt+1, cmp2);//這裡的sort複雜度會多一個log for(int i=1; i<=cnt; i++){ for(int j=i+1; j<=cnt && P[tmp[j]].y - P[tmp[i]].y < d; j++){ double d1 = calc(tmp[i], tmp[j]); d = min(d, d1); } } return d; } int main(){ cin >> n; for(int i=1; i<=n; i++){ cin >> P[i].x >> P[i].y; } sort(P+1, P+n+1, cmp); cout << fixed << setprecision(4) << solve(1, n); return 0; }
總結
解法還是很妙的,主要是對求解過程可行性剪枝,這種分治思想也很值得學習。