题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007
题目大意:N个点,求最近一对点的距离的一半。
关键思想:最傻瓜式的枚举所有点对的距离找最短 O(n^2),会TLE。
分治可以优化成O(nlogn)。二分区间,考虑三种情形:点对的两点都在左区间、两点都在右区间、两点一左一右
前两种情况,可以递归地解出来,分别为d1,d2。第三种,可以依据min(d1,d2)收缩成一条带状区域(勾股定理显然)。
然后对带中所有点进行处理,此时又可依据min(d1,d2)优化。可以证明,在x方向为2d,y方向为d的矩形区域内,最多有7个点,否则最小距离一定大于d。
代码如下:
//分治 参数要确认是否为闭区间
#include <iostream>
#include <algorithm>
#include <vector>
#include <math.h>
using namespace std;
int N;
struct point{
double x,y;
}egx[100010],egy[100010];
bool cmpx(point x,point y){
return x.x<y.x;
}
bool cmpy(point x,point y){
return x.y<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 Clodis(int l,int r){
if(l+1==r)return dis(egx[l],egx[r]);//只有两个或三个点,直接穷举
if(l+2==r)return min(dis(egx[l],egx[r]),min(dis(egx[l],egx[l+1]),dis(egx[l+1],egx[r])));
int cnt=0,mid=l+r >> 1;
double d=min(Clodis(l,mid),Clodis(mid+1,r)); //递归的过程解
for(int i=l;i<=r;i++)
if(fabs(egx[i].x-egx[mid].x)<=d)egy[cnt++]=egx[i]; //带状区域内的点放入egy数组
sort(egy,egy+cnt,cmpy); //对y进行排序 ,以后只需顺次遍历少数点
for(int i=0;i<cnt;i++){
//两种遍历方式都可以
// for(int j=i+1;j<cnt;j++){
// if(egy[j].y-egy[i].y>=d)break;
// d=min(dis(egy[i],egy[j]),d);
// }
for(int j=1;i+j<cnt&&j<8;j++){
if(i!=j)
d=min(d,dis(egy[i],egy[j]));
}
}
return d;
}
int main(){
double max=1e19;
while(~scanf("%d",&N)&&N){
max=1e19;
for(int i=0;i<N;i++)
scanf("%lf%lf",&egx[i].x,&egx[i].y);
sort(egx,egx+N,cmpx);
printf("%.2lf\n",Clodis(0,N-1)/2);
}
return 0;
}