zoukankan      html  css  js  c++  java
  • 平面最近点对(学习笔记)

    先来推荐几篇.其实大家只要认真看了这些博客,就没必要看我的了QWQ.

    平面最近点对问题:在同一平面内的所有点中,找出距离最近的两个点.

    既然有模板题,还是双倍经验,那就直接来讲吧.首先暴力应该都想得到,就是(n^2)枚举所有点,算出所有点两两之间的距离,同时比较大小,记录答案.

    考虑如何优化?核心思想是分治.至于为什么要用分治,介绍算法框架的时候自然而然就明白了.

    设solve(V)为点集V中的最近点对的距离,那么,要求点集V的最近点对,我们先对其按x坐标从小到大排序,然后考虑选择一条垂直分割线,把V划分为点数尽可能相等的两部分V1和V2(说直白了就是选择点集V最中间两个点的中线),则solve(V)=min{solve(V1),solve(V2), dis(u,v)⁡|⁡u∈V1,v∈V2}.

    如何理解这个式子?关于点集V中的最近点对,无非只有三种情况,两个点都在V1中,两个点都在V2中,或者一个点在V1中,另一个点在V2中.前两种情况就是分治的子问题,我们递归下去求解就好了.那么第三种情况如何高效求解呢?

    令d=min{solve(V1),solve(V2)},那么我们可以只需要考虑已经选择好的垂直分割线往两端各延伸d的这一个区域,因为只有这个区域内的点对才可能有比d更小的距离(这里不懂的话,画个图很快就明白了)

    
    solve(V){
    	选择一条垂直分割线,它将V分割为V1,V2;
    	d=min(solve(V1),solve(V2));
    	取出垂直分割线往两端延伸d的区域内的所有点;
    	求出区域内最近距离d’;
    	return min(d,d’);
    }
    
    

    到了这里,问题只剩最后一步,如何求出区域内最近距离d’,上面那一段文字只是优化了求解的时间复杂度,仍未提到具体地如何做?对于区域内的点,我们对其按y坐标排序;对于每个点,找到y坐标与它相差不超过d的点,计算距离d’,更新答案.

    对y排序时,sort排序,总的时间复杂度(nlog_nlog_n):

    int n,b[200005];
    struct point{
        double x,y;
    }a[200005];
    bool cmpx(point x,point y){return x.x<y.x;}
    bool cmpy(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));
     }
    //l,r是点的下标,表示本次处理由第l~r个点构成的点集
    double solve(int l,int r){
        if(l==r)return 1e18;
        if(l+1==r)return dis(a[l],a[r]);
    //亲测:这一句可以不写,上面那个边界条件必须要写
        int mid=(l+r)>>1;
    //找到分割点,即点集中最中间的点
        double d=min(solve(l,mid),solve(mid+1,r));
    //分治
        int k=0;
        for(int i=l;i<=r;i++)
        	if(fabs(a[i].x-a[mid].x)<=d)b[++k]=i;
        sort(b+1,b+k+1,cmpy);
        for(int i=1;i<=k;i++)
        for(int j=i+1;j<=k;j++)
            if(a[b[j]].y-a[b[i]].y<=d)
            	d=min(d,dis(a[b[i]],a[b[j]]));
        return d;
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++)
        	scanf("%lf%lf",&a[i].x,&a[i].y);
        sort(a+1,a+n+1,cmpx);
        printf("%.4lf
    ",solve(1,n));
        return 0;
    }
    
    

    优化:对y排序时,归并排序,总的时间复杂度(nlog_n)(亲测:快了100+ms)

    int n;
    struct point{
        double x,y;
    }a[200005],b[200005];
    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 solve(int l,int r){
        if(l==r)return 1e18;
        int mid=(l+r)>>1;
        double midline=(a[mid].x+a[mid+1].x)/2;
    //比较两份代码,就会发现,上面的分割线直接就是a[mid].x
    //而这里的分割线是(a[mid].x+a[mid+1].x)/2
    //显然此处更为严谨,这里以a[mid].x作为分割线会WA
        double d=min(solve(l,mid),solve(mid+1,r));
        int i=l,j=mid+1,k=i;
        while(i<=mid&&j<=r){
    		if(cmpy(a[i],a[j]))b[k]=a[i],i++,k++;
    		else b[k]=a[j],j++,k++;
        }
        if(i<=mid)while(i<=mid)b[k]=a[i],i++,k++;
        else while(j<=r)b[k]=a[j],j++,k++;
        for(int i=l;i<=r;i++)a[i]=b[i];
        int L=1,R=0;
        for(int i=l;i<=r;i++)
    	if(fabs(a[i].x-midline)<=d){
    	    while(L<=R&&(a[i].y-b[L].y)>=d)L++;
    //优化时间复杂度,把一定不符合要求的点先去掉再进循环.
    //这里没有的话就会TLE
    	    for(int j=L;j<=R;j++)
    			d=min(d,dis(a[i],b[j]));
    	    b[++R]=a[i];
    	}
        return d;
    }
    

    sort排序的写法比归并排序更加直接简单,后者细节较多,但时间效率更高,一般而言,sort排序就足够了.

  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10363315.html
Copyright © 2011-2022 走看看