平面最近点对
题目描述
给定平面上n个点,找出其中的一对点的距离,使得在这n个点的所有点对中,该距离为所有点对中最小的
输入输出格式
输入格式:第一行:n;2≤n≤200000
接下来n行:每行两个实数:x y,表示一个点的行坐标和列坐标,中间用一个空格隔开。
输出格式:仅一行,一个实数,表示最短距离,精确到小数点后面4位。
输入输出样例
3 1 1 1 2 2 2
1.0000
说明
0<=x,y<=10^9
用分治解决这个问题。
我们正在计算区间$[L, R]$内的答案,假设我们已经计算出了$[L, mid]$和$[mid+1,R]$的答案,我们要求出$[L,R]$答案。
我们设$[L, mid]$的答案是$d1$,$[mid+1,R]$的答案是$d2$,那么区间[L,R]的答案$d=min(d1, d2)$。
这是显然不对的。
为了方便,一下用$S1$代指$[L, mid]$区间,用$S2$代指$[mid+1,R]$区间。
因为我们只考虑了两个区间内部点的答案,跨区间的两个点有可能成为答案。
我们设一个在$S1$中的点为$p1$,在$S2$中的点为$p2$。
那么如果我们暴力枚举,就又成$O(N^2)$的了。
但我们发现,我们现在的答案已经是$d$了,所以我们根本没必要枚举不可能成为答案的点。
假如区间中点是$mid$。
那有用的点其实都在$[mid-d, mid+d]$之间,但是这样最坏情况下还是$O(N^2)$的。
我们又发现,对于一个点$p1$,可能成为答案的$p2$的$y$坐标的范围,又在$[y[p1]-d, y[p1]+d]$之间。
对于每个点$p1$,这样的区间是$d \times 2d$这么大的。
因为我们求出最小距离是$d$,那么在这个有效的区间内最多只有6个点,这个可以画图感受一下。
所以统计跨越区间的贡献的复杂度变成了线性。
因为每次分治还要排序,所以总体复杂度是$O(Nlog_2N)$的。
好像可以边分治边归并,可以去掉一个log,不太了解。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <queue> #include <algorithm> #include <cmath> using namespace std; #define reg register #define gc getchar inline int read() { int res=0;char ch=gc();bool fu=0; while(!isdigit(ch)){if(ch=='-')fu=1;ch=gc();} while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc(); return fu?-res:res; } int n; struct date { double x, y; }da[200005]; int tmp[200005]; bool cmp1(date a, date b) { return a.x < b.x; } bool cmp2(int a, int b) { return da[a].y < da[b].y; } inline double dist(int i, int j) { return sqrt((da[i].x - da[j].x) * (da[i].x - da[j].x) + (da[i].y - da[j].y) * (da[i].y - da[j].y)); } double ans; double Solve(int L, int R) { double d = 1e20 * 1.0; if (L == R) return d; if (L + 1 == R) return dist(L, R); int mid = (L + R) >> 1; d = min(Solve(L, mid), Solve(mid + 1, R)); int cnt = 0; for (reg int i = L ; i <= R ; i ++) if (fabs(da[i].x - da[mid].x) <= d) tmp[++cnt] = i; sort(tmp + 1, tmp + 1 + cnt, cmp2); for (reg int i = 1 ; i <= cnt ; i ++) for (reg int j = i + 1 ; j <= cnt and da[tmp[j]].y - da[tmp[i]].y <= d ; j ++) d = min(d, dist(tmp[i], tmp[j])); return d; } int main() { n = read(); for (reg int i = 1 ; i <= n ; i ++) scanf("%lf%lf", &da[i].x,&da[i].y); sort(da + 1, da + 1 + n, cmp1); printf("%.4lf\n", Solve(1, n)); return 0; }