zoukankan      html  css  js  c++  java
  • 平面最近点距离问题(分治法)

    算法:   

     0:把所有的点按照横坐标排序   

    1:用一条竖直的线L将所有的点分成两等份  

     2:递归算出左半部分的最近两点距离d1,右半部分的最近两点距离d2,取d=min(d1,d2)   

    3:算出“一个在左半部分,另一个在右半部分”这样的点对的最短距离d3。

      4:结果=min(d1,d2,d3)    

    关键就是这第3步。貌似这需要n^2的时间,把左边每个点和右边每个点都对比一下。其实不然。秘密就在这里。    首先,两边的点,与分割线L的距离超过d的,都可以扔掉了。   其次,即使两个点P1,P2(不妨令P1在左边,P2在右边)与分割线L的距离(水平距离)都小于d,如果它们的纵坐标之差大于d,也没戏。  就是这两点使得搜索范围大大减小:   对于左半部分的,与L的距离在d之内的,每个P1来说:右半部分内,符合以上两个条件的点P2最多只有6个!  原因就是:   d是两个半平面各自内,任意两点的最小距离,因此在同一个半平面内,任何两点距离都不可能超过d。   我们又要求P1和P2的水平距离不能超过d,垂直距离也不能超过d,在这个d*2d的小方块内,最多只能放下6个距离不小于d的点。    因此,第3步总的比较距离的次数不超过n*6。   

     第3步的具体做法是:    

    3.1 删除所有到L的距离大于d的点。 O(n)  

     3.2 把右半平面的点按照纵坐标y排序。 O(nlogn)  

     3.3 对于左半平面内的每个点P1,找出右半平面内纵坐标与P1的纵坐标的差在d以内的点P2,计算距离取最小值,算出d3。 O(n*6) = O(n)    因为3.2的排序需要O(nlogn),   所以整个算法的复杂度就是O(n((logn)^2))。    

    /** 
    最近点对问题,时间复杂度为O(n*logn*logn) 
    */  
    #include <iostream>  
    #include <cstdio>  
    #include <cstring>  
    #include <cmath>  
    #include <algorithm>  
    using namespace std;  
    const double INF = 1e20;  
    const int N = 100005;  
      
    struct Point  
    {  
        double x;  
        double y;  
    }point[N];  
    int n;  
    int tmpt[N];  
      
    bool cmpxy(const Point& a, const Point& b)  
    {  
        if(a.x != b.x)  
            return a.x < b.x;  
        return a.y < b.y;  
    }  
      
    bool cmpy(const int& a, const int& b)  
    {  
        return point[a].y < point[b].y;  
    }  
      
    double min(double a, double b)  
    {  
        return a < b ? a : b;  
    }  
      
    double dis(int i, int j)  
    {  
        return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)  
                    + (point[i].y-point[j].y)*(point[i].y-point[j].y));  
    }  
      
    double Closest_Pair(int left, int right)  
    {  
        double d = INF;  
        if(left==right)  
            return d;  
        if(left + 1 == right)  
            return dis(left, right);  
        int mid = (left+right)>>1;  
        double d1 = Closest_Pair(left,mid);  
        double d2 = Closest_Pair(mid+1,right);  
        d = min(d1,d2);  
        int i,j,k=0;  
        //分离出宽度为d的区间  保存到tmpt中 
        for(i = left; i <= right; i++)  
        {  
            if(fabs(point[mid].x-point[i].x) <= d)  
                tmpt[k++] = i;  
        }  
        sort(tmpt,tmpt+k,cmpy);  
        //暴力搜索 tmpt中的最短距离
        for(i = 0; i < k; i++)  
        {  
            for(j = i+1; j < k && point[tmpt[j]].y-point[tmpt[i]].y<d; j++)  
            {  
                double d3 = dis(tmpt[i],tmpt[j]);  
                if(d > d3)  
                    d = d3;  
            }  
        }  
        return d;  
    }  
      
      
    int main()  
    {  
        while(true)  
        {  
            scanf("%d",&n);  
            if(n==0)  
                break;  
            for(int i = 0; i < n; i++)  
                scanf("%lf %lf",&point[i].x,&point[i].y);  
            sort(point,point+n,cmpxy);  
            printf("%.2lf
    ",Closest_Pair(0,n-1));  
        }  
        return 0;  
    }  
  • 相关阅读:
    【转】《基于MFC的OpenGL编程》Part 5 Transformations Rotations, Translations and Scaling
    【转】 《基于MFC的OpenGL编程》Part 10 Texture Mapping
    【转】 《基于MFC的OpenGL编程》Part 11 Blending, Antialiasing and Fog
    win form 托盘功能的实现(引用CSDN)
    C# win form退出窗体时对话框实用
    智能DNS 笔记
    iis无法启动, 找出占用80端口的罪魁祸首
    gvim for windows的剪贴板操作
    内容交换
    Content Networking 读书笔记
  • 原文地址:https://www.cnblogs.com/zle1992/p/8933421.html
Copyright © 2011-2022 走看看