原题链接:P1429 平面最近点对(加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:给定多个点,求最小的两点距离(x, y 都大于等于0)
思路
我看不出来可以分治做,其实是最简单的分治思想,即对空间做分治,查找最近的两点,分治成查找不同空间范围的最近两点,再比较区域交叉的位置,很像最大子段和
难的地方在分析 首先是一分二,按x(或y)排序后分成两个区域,分别求最近点,然后剩下的可能是最近点的点必然在mid的两侧,且与mid之间距离要小于已经找到的两点最短距离
这里有一步是缩小搜索空间,第三个空间局限在和mid排序方向上距离 小于 已找到最小距离d的点,对这些点做查找
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, INF = 2 << 19;
struct S{
double x, y;
bool operator < (const S &w) const
{
return x < w.x;
}
}S[N];
bool cmp(int a, int b)
{
return S[a].y < S[b].y;
}
double dist(int a, int b)
{
return sqrt((S[a].x - S[b].x) * (S[a].x - S[b].x) + (S[a].y - S[b].y) * (S[a].y - S[b].y));
}
int n;
int t[N];
double merge(int l, int r)
{
double d = INF;
if(l == r) return d;
else if(r - l == 1) return dist(l, r);
int mid = l + r >> 1;
double d2 = merge(l, mid), d3 = merge(mid + 1, r);
d = min(d2, d3);
int j = 0;
for(int i = l; i < r; i ++)
{
if(fabs(S[i].x - S[mid].x) < d) t[j ++] = i;
}
sort(t, t + j, cmp);
for(int i = 0; i < j; i ++)
for(int k = i + 1; k < j && fabs(S[t[i]].y - S[t[k]].y) < d; k ++)
{
if(i != k)
{
d = min(d, dist(t[i], t[k]));
}
}
return d;
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> S[i].x >> S[i].y;
}
sort(S, S + n);
double ans = merge(0, n - 1);
printf("%.4lf\n", ans);
return 0;
}