1. 程式人生 > >分治入門——平面分治

分治入門——平面分治

分治思想:劃分子問題,解決子問題,合併子問題

題目:UVA 10245
題意:在一個二維平面內給定n個點,求最近的兩個點的距離。(n≤10000)

題解:直接暴力列舉所有點是肯定行不通的。那麼基於分治的思想:按照橫座標排序後,分成兩個部分,那麼最近距離的點對就是以下的情況
(1)兩個點均屬於一個區域
(2)兩個點屬於不同區域
線歪了。。。
對於(1)的情況,我們可以直接遞迴求得,因此關鍵在於對於第二情況的處理,直接處理的話並沒有比原來問題簡單多少。因此將(2)的描述修改一下

(·2)兩個點來自不同區域,並且橫座標距離<d(d為情況(1)的最小長度)

在(2)的基礎上加了橫座標距離<d的限制。因為在(1)之中我們已經找到了最短距離d,因此橫座標距離 ≥ d也就無需再考慮。

首先,所有的點已經以橫座標為關鍵字排序完成,取劃分界限mid,從mid往左列舉到mid的橫座標距離<d的點,再列舉mid往右邊走到mid的橫座標距離<d的點,對它們進行一一配對求得答案。就把整個平面縮成了[mid-d, mid+d],只需要考慮區間裡面的點即可。

(或許大家比較擔心一種情況,萬一初現這個區間內的點很多呢?但是書的回答是:“d經過了很多次的遞迴變得很小,所以不考慮”)

這樣,遞迴的深度是O(logn),每層處理複雜度為O(n),而總的時間複雜度就是O(nlogn)

原始碼:

#include <stdio.h>
#include <math.h>
#include <algorithm> using namespace std; const int N = 1e4+5; const int inf = 0x3f3f3f3f; struct node { double x, y; }a[N]; bool cmpx(node a, node b) { return a.x<b.x; } double dist(node a, node b) { return sqrt(pow(a.x-b.x, 2)+pow(a.y-b.y, 2)); } double solve(int l, int r) { if
(l==r) return inf; if(l+1==r) return dist(a[l], a[r]); //劃分解決 int mid = (l+r)>>1; double st = a[mid].x;//劃分依據 double d = min(solve(l, mid), solve(mid+1, r)); //在矩形空間內,左邊的點跟右邊的點一一匹配,合併 for(int i = mid; i >= l && st-a[i].x < d; i --) for(int j = mid+1; j <= r && a[j].x-st < d; j ++) d = min(d, dist(a[i], a[j])); return d; } int main() { int n; while(scanf("%d", &n), n) { for(int i = 0; i < n; i ++) scanf("%lf%lf", &a[i].x, &a[i].y); sort(a, a+n, cmpx); double ans = solve(0, n-1); if(ans<10000.0) printf("%.4f\n", ans); else puts("INFINITY"); } return 0; }