平面最近点对问题(分治思想)
平面最近点对顾名思义就是在一个二维平面内找到距离最近的两个点。用到分治思想,可以很好的减少复杂度。
接下来进行解析。
- 首先将输入的坐标按照x坐标大小排序,并二分为左右两块区域
- 在左右两块区域都可以得到一个最近点对距离,取最小值作为暂时的ans
- 考虑完左右两边的情况以后再考虑两个点分别在左右的情况,很显然这种情况就不用所有点都考虑,那就没必要二分了。我们以中间的点为标准,划分一个新区域,区域范围为中间点向左右都取ans’的大小
- 为了进一步减少计算量,我们可以在y坐标上下功夫,可以只考虑两点垂直距离不超过ans’大小的两个点,因为超过的话按勾股定理两点距离是不可能比ans’更小的。
- 最后,取min(ans3,ans’)即可得出答案
接下来是源代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 10000
#define INF 0x7fffffff
struct point{
double x,y;
}a[MAXN];
int N;
int t[MAXN];
//以x坐标大小为关键词
bool cmp1(point x,point y){
return x.x<y.x;
}
//以y坐标大小为关键词
bool cmp2(int x,int y){
return a[x].y<a[y].y;
}
//计算两点距离
double dis(point x,point y){
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
//核心算法 分治思想
double F(int l,int r){
if(r-l==0)
return INF;
if(r-l==1) //如果递归完后直接输出距离
return dis(a[l],a[r]);
int mid=(l+r)>>1;
double ans=min(F(l,mid),F(mid+1,r));
int cnt=0;
for(int i=l;i<=r;i++)
//还有一种情况是距离最小的两点刚好分在mid两端ans距离内的点
if(a[i].x>=a[mid].x-ans&&a[i].x<=a[mid].x+ans)
t[++cnt]=i;
sort(t+1,t+cnt+1,cmp2); //以y坐标大小排序
for(int i=1;i<=cnt;i++)
for(int j=i+1;j<=cnt;j++){
if(a[t[j]].y>=a[t[i]].y+ans) break; //两个点的垂直距离超过ans就不必计算了,显然不可能会成为新的ans
ans=min(ans,dis(a[t[i]],a[t[j]]));
}
return ans;
}
int main(){
cin >> N; //输入坐标数
for(int i=1;i<=N;i++)
cin >> a[i].x >> a[i].y; //输入坐标
sort(a+1,a+N+1,cmp1); //以x坐标大小排序
cout << F(1,N) << endl; //输出最小点对距离
}