解法:分而治之
分治
- 数组
按x升序排序,调用递归函数
。
是为了得到区间的最近点对的长度。处理如下
- 以中间的数字
为中心线进行分割,分别对
和
进行处理。
- 如果区间的长度比较小,可直接
暴力计算,比如以下是
。
合并
- 已知两边的最近点对的距离为mindist。现在mid两边各选择一个距离才有可能小于mindist。
- 我们把两边距离中心线距离小于mindist的点挑选出放入数组t。
- 对数组t按y进行升序,方便以下处理。
- 每次选择t中的一个点pi,再按顺序挑选pj,确定最短距离,直到不小于mindist。
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 200000 + 10;
double const inf = 1e20;
struct Point{
double x,y;
}p[N],t[N];
double mindist = inf; //全局最小值,如果此次合并都比它大,就不用考虑
bool cmpx(Point a,Point b){
return a.x < b.x;
}
bool cmpy(Point a,Point b){
return a.y < b.y;
}
double dist(Point p[],int i,int j){
return sqrt((p[i].x - p[j].x) * (p[i].x - p[j].x) +
(p[i].y - p[j].y) * (p[i].y - p[j].y));
}
void rec(int l,int r){
if(r - l <= 3){
for(int i=l;i<=r;i++){
for(int j=i+1;j<=r;j++){
double d = dist(p,i,j);
if(d < mindist) mindist = d;
}
}
return;
}
int mid = (l + r) >> 1;
int midx = p[mid].x;
rec(l,mid); rec(mid + 1,r);
int cnt = 0;
for(int i=l;i<=r;i++){ //求出所有点到midx的距离<mindist的点
if(fabs(p[i].x - midx) < mindist) t[cnt++] = p[i];
}
sort(t,t + cnt,cmpy);
for(int i=0;i<cnt;i++){
for(int j=i+1;j<cnt;j++){
double d = dist(t,i,j);
if(d < mindist) mindist = d;
else break;
}
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
sort(p,p + n,cmpx);
rec(0,n-1);
printf("%.4f\n",mindist);
return 0;
}