1. 程式人生 > >Luogu 1429 平面最近點對 | 平面分治

Luogu 1429 平面最近點對 | 平面分治

www poi alt spa fine 左右 str orm 修改

Luogu 1429 平面最近點對

題目描述
給定平面上n個點,找出其中的一對點的距離,使得在這n個點的所有點對中,該距離為所有點對中最小的
輸入輸出格式
輸入格式:
第一行:n;2≤n≤200000
接下來n行:每行兩個實數:x y,表示一個點的行坐標和列坐標,中間用一個空格隔開。
輸出格式:
僅一行,一個實數,表示最短距離,精確到小數點後面4位。

這是一道平面上的分治。

這是一個平面,我們把它分成兩半,使x坐標位於最中間的兩個點分到左右兩側:

技術分享圖片

對於同在左側或同在右側的點對,我們可以遞歸處理;對於分別位於兩側的點對,如何處理呢?

設遞歸處理後我們知道同在左側和同在右側的點對中,最小距離是d;那麽需要枚舉的“分別位於兩側的點對”的兩個端點的橫坐標一定都位於中線左/右距離不超過d的範圍內。

當枚舉左側的一個點的時候,右側只需要找y坐標更小,且y坐標相差不超過d的點,與左側的點配對。

有了以上兩條限制,對於一個點p,另一側需要與它配對的點不超過6個。

技術分享圖片

至於具體實現,要先把所有點按照x坐標排序,然後再遞歸的過程中按照y坐標排序。子區間內部點的順序被修改(從按x排序變成按y排序),並不會影響母區間的劃分,因為在遞歸進入子區間前母區間已經劃分好了。

AC代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define space putchar(' ')
#define enter putchar('\n') #define INF 0x3f3f3f3f using namespace std; typedef long long ll; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while
(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 200005; int n; struct point { double x, y; point operator - (const point &b){ return (point){x - b.x, y - b.y}; } double norm(){ return sqrt(x * x + y * y); } bool operator < (const point &b) const{ return x < b.x; } } p[N], a[N], b[N], c[N]; double solve(int l, int r){ if(l >= r) return 1e20; int mid = (l + r) >> 1; double xmid = (p[mid].x + p[mid + 1].x) / 2; double d = min(solve(l, mid), solve(mid + 1, r)); int pos = l, pb = 0, pc = 0, pl = l, pr = mid + 1; while(pos <= r){ if(pl <= mid && (pr > r || p[pl].y < p[pr].y)){ if(p[pl].x > xmid - d) b[++pb] = p[pl]; a[pos++] = p[pl++]; } else{ if(p[pr].x < xmid + d) c[++pc] = p[pr]; a[pos++] = p[pr++]; } } for(int i = l; i <= r; i++) a[i] = p[i]; for(int i = 1, j = 1; i <= pb || j <= pc;){ if(i <= pb && (j > pc || b[i].y < c[j].y)){ for(int k = j - 1; k && b[i].y - c[k].y < d; k--) d = min(d, (b[i] - c[k]).norm()); i++; } else{ for(int k = i - 1; k && c[j].y - b[k].y < d; k--) d = min(d, (c[j] - b[k]).norm()); j++; } } return d; } int main(){ read(n); for(int i = 1; i <= n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); sort(p + 1, p + n + 1); printf("%.4lf\n", solve(1, n)); return 0; }

Luogu 1429 平面最近點對 | 平面分治