这道题《算法导论》和《编程之美》都有详细的讲解
分治思想:先把n个点按x坐标排序,然后求左边n/2个和右边n/2个的最近距离,最后合并。
合并要考虑 最近点对 的其中一个点在左边,另一个在右边的情况。
如上图,只需考虑[x-minDist,x+minDist]的这段区域minDist*(2*minDist)的矩形区域,这样一个区域最多只有八个点(反推),所以只用搜索按坐标y排序后紧随其后的7个点即可。0和1,2,3,4....;1和2,3,4...
这里有详细的证明点击打开链接
总的算法复杂度为O(nlogn)
hdu上面这道题用cin读取数据TLE了,改为scanf就AC了
#include <iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=100005;
struct Point{
public:
double x,y;
};
Point v[maxn];
int ind[maxn];
bool cmpx(const Point &p1,const Point &p2){
return p1.x<p2.x;
}
bool cmpy(int i1,int i2){
return v[i1].y<v[i2].y;
}
double dis(const Point& p1, const Point& p2){
return sqrt((p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
double min(double a,double b)
{
return a<b?a:b;
}
double devide(int l,int r)
{
if(r==1+l)return dis(v[r],v[l]);
if(r==2+l)return min(min(dis(v[l],v[l+1]),dis(v[l+1],v[r])),dis(v[l],v[r]));
int mid=(l+r)/2;
int c=0;
double minDis=min(devide(l,mid),devide(mid+1,r));
for(int i=l;i<=r;++i){
if(v[i].x<=v[mid].x+minDis&&v[i].x>=v[mid].x-minDis){
ind[c++]=i;
}
}
sort(ind,ind+c,cmpy);
for(int i=0;i<c;++i){
for(int j=i+1;j<c;++j){
if(v[ind[j]].y-v[ind[i]].y>=minDis)break;
minDis=min(minDis,dis(v[ind[i]],v[ind[j]]));
}
}
return minDis;
}
int main()
{
//freopen("in.txt","r",stdin);
int n;
while(cin>>n&&n){
double x,y;
for(int i=0;i<n;++i){
scanf("%lf %lf",&v[i].x,&v[i].y);
}
sort(v,v+n,cmpx);
printf("%.2lf%\n",devide(0,n-1)/2);
}
return 0;
}