分治入門——平面分治
阿新 • • 發佈:2019-01-29
分治思想:劃分子問題,解決子問題,合併子問題
題目: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;
}