平面分治 平面上的最近点对问题

平面上有(n)个点,计算距离最近的两个点之间的距离

将所有点按照横坐标(x_0)分成左右两半,那么距离最近的点对一定是下面两种情况中的最小值:
1.两点都属于左半边或者右半边
2.两点一个属于左半边,一个属于右半边
第一种情况可以通过递归来处理,由于计算的是最小值,假设第一情况得到的最小值为(d),那么第二种情况可以等价于所有两分属左右两边,并且距离小于(d)的点对距离的最小值
设分治时中点横坐标为(x_0),则满足条件的点的横坐标取值范围为(x_0-d<x<x_0+d),将本次分治涉及到的所有点按照纵坐标从小到大排序,对于每个点,计算它与纵坐标小于等于它本身的点组成的点对距离,设当前点的纵坐标为(y_p),则需要考虑纵坐标满足条件(y_p-d<y<y_p)的点
所以对于每个点,每次需要检查的另一个点在(x_0-d<x<x_0+d,y_p-d<y<y_p)的矩形之内,有结论表示这样的矩形区域内的点不超过6个
所以首先按照横坐标将所有点排序,每次选取中点进行分治并且更新答案(情况1),归并的时候按照纵坐标排序,扫描所有点,找到所有满足要求的点对(不超过6个),更新答案(情况2)
递归深度(O(log n)),每一层操作的时间复杂度(O(n)),总的时间复杂度(O(nlog n))

模板题:hdu1007 Quoit Design
平面上有(n)个点,环内最多可以套中一个点,计算环的最大半径

环的最大半径为平面上最近点对距离的二分之一

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define PDD pair<double,double>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

const int maxn=100010;
int n;
PDD a[maxn];

bool cmp_y(PDD s1,PDD s2){
    return s1.second<s2.second;
}

double closest_pair(PDD *a,int n){
    if(n<=1) return 10010;
    int m=n/2;
    double x=a[m].first;
    double d=min(closest_pair(a,m),closest_pair(a+m,n-m));
    inplace_merge(a,a+m,a+n,cmp_y);
    vector<PDD> b;
    for(int i=0;i<n;i++){
        if(fabs(a[i].first-x)>=d) continue;
        int l=b.size();
        for(int j=0;j<l;j++){
            double dx=a[i].first-b[l-1-j].first;
            double dy=a[i].second-b[l-1-j].second;
            if(dy>=d) break;
            d=min(d,sqrt(dx*dx+dy*dy));
        }
        b.push_back(a[i]);
    }
    return d;
}

void solve(){
    sort(a,a+n);
    double ans=closest_pair(a,n);
    printf("%.2f
",ans/2);
}

int main(){
    while(scanf("%d",&n)!=EOF && n){
        for(int i=0;i<n;i++) scanf("%lf %lf",&a[i].first,&a[i].second);
        solve();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/fxq1304/p/13454031.html