hdu1007 找最近点对问题(一)

来源:互联网 发布:合同管理数据库 编辑:程序博客网 时间:2024/05/22 08:08

题目链接:hdu1007

题目大意:给出n个点,求出最近的两个点的距离,最后输出时候这个距离再除以2.

思路:

   蛮力算法复杂度很明显为O(N^2)不理想。如果是O(N*logN)就好多了,下面介绍分治算法在解决该问题的具体应用过程。

       假设平面上的点按x排序好了,这样最多增加O(N*logN),这再整个算法来看并没有增加复杂度级别。
       排好序后,可以划一条垂线,把点集分成两半:PL和PR。于是最近点对或者在PL中,或者在PR中,或者PL,PR各有一点。
       把三种距离情况定义为dL, dR, dC.
 
             
       其中dL, dR可以递归求解,于是问题就变为计算dC。 根据上面红色字解释,由于我们希望得到O(N*logN)的解,因此必须能够仅仅多花O(N)的附加工作计算dC。
       另s=min(dL, dR). 通过观察能得出结论:如果dC<s,即dC对s有所改进,则只需计算dC。如果dC满足这样的条件,则决定dC的两点必然在分割线的s距离之内,称之为带(strip)
       否则不可能满足dC<s, 于是缩小了需要考虑的点的范围。      
         
       如果是均匀分布的点集,则能证明出在该带中平均只有O(sqrt(N))个点。因此,对这些点运用蛮力法可以在O(N)时间内完成。
       但是容易TLE,所以需要进行优化。蛮力枚举的话最坏情况复杂度仍会上升至O(N*N), 为了得到O(N*logN)解法,我们仍然需要进行优化。通过进一步观察,我们发现,在带中的点,若进行按y坐标排序后,如果两个点y坐标相差s,则一定不是最短点对,所以只需求y相差不大于s的点对距离即可。
#include <stdio.h>#include <math.h>#include <algorithm>using namespace std;#define N 100001struct Point{    double x,y;}p[N];int arr[N];double Min(double a,double b){    return a<b?a:b;}// 求两点之间的距离double dis(Point a,Point b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}// 根据点横坐标or纵坐标排序bool cmp_y( int a,int b){    return p[a].y<p[b].y;}bool cmp_x( Point a,Point b){    return a.x<b.x;}// 求最近点对double close_pair( int l,int r ){    // 判断两个点和三个点的情况    if( r==l+1 )    return dis( p[l],p[r] );    else if( r==l+2 )   return Min( dis(p[l],p[r]),Min( dis(p[l],p[l+1]),dis(p[l+1],p[r]) ) );    int mid=(l+r)>>1;    double ans=Min(close_pair(l,mid),close_pair(mid+1,r));    int i,j,cnt=0;    // 如果 当前p[i]点 横坐标位于 范围(中点横坐标-ans,中点横坐标+ans)位置内,则记录点的序号    for(i=l; i<=r; ++i)        if(  p[i].x>=p[mid].x-ans && p[i].x<=p[mid].x+ans  )            arr[cnt++]=i;    // 按照纵坐标由小到大 对于arr数组内点进行排序    sort(arr,arr+cnt,cmp_y);    for(i=0; i<cnt; i++)        for(j=i+1; j<cnt; j++)        {            if(p[arr[j]].y-p[arr[i]].y>=ans) break;            ans=Min(ans,dis(p[arr[i]],p[arr[j]]));        }    return ans;}int main(){    int i,n;    while( scanf("%d",&n)!=EOF && n)    {        for(i=0;i<n;++i)            scanf("%lf%lf",&p[i].x,&p[i].y);        // 先将所有点按照横坐标由小到大排序        sort(p,p+n,cmp_x);        printf("%.2lf\n",close_pair(0,n-1)/2.0);    }    return 0;}



原创粉丝点击