Luogu 1429 平面最近點對 | 平面分治
阿新 • • 發佈:2017-11-21
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 平面最近點對 | 平面分治