平面最近点距离问题(分治法)

大憨熊 提交于 2020-03-15 10:11:12

算法:   

 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\n",Closest_Pair(0,n-1));  
    }  
    return 0;  
}  

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!