题目链接: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; }