初见 | 平面最近点对

闲来无事,学个新东西。

问题概述

求有 \(n\) 个点的平面上欧几里得距离最近的两个点。

算法

考虑分治,将当前的点集经过按照 \(x\) 坐标为关键字排序之后,不断的分为两段分别求解再合并。

分治是好想的,问题是如何合并。

假设我们要合并的两个区间的最近距离分别是 \(h_1,h_2\),取较小值之后记作 \(h\),区间中点为 \(mid\)。然后将所有满足 \(| x_mid - x_i | < h\) 的点放到一个新的集合中。

然后再将这个集合按照 \(y\) 为关键字排序,然后每次枚举两个点,只用满足 \(| y_j - y_i | < h\) 的点更新答案。

而最后这个满足条件的点集的大小最大为 \(7\)OI-wiki 上有其证明

Code

这个是洛谷模板题 P1429的代码,复杂度为 \(O(n \log^2 n)\),使用缺省源「V5.2」

template<typename J>
I J Hmin(const J &x,const J &y)
{
    Heriko x<y?x:y;
}

CI MXX(2e5+1);
const double INF(1e12);

I bool CMP(const pair<double,double> &a,const pair<double,double> &b)
{
    Heriko a.second<b.second;
}

I double Dist(const pair<double,double> &a,const pair<double,double> &b)
{
    Heriko sqrt((long double)(a.first-b.first)*(a.first-b.first)+(long double)(a.second-b.second)*(a.second-b.second));
}

int n;

pair<double,double> a[MXX],tmp[MXX];

double Merge(int l,int r)
{
    if(l==r)
        Heriko INF;

    int mid((l+r)>>1),top(0);
    double dis(INF);
    dis=Hmin(Merge(l,mid),Merge(mid+1,r));

    for(int i(l);i<=r;++i)
        if(fabs(a[mid].first-a[i].first)<dis)
            tmp[++top]=a[i];

    std::sort(tmp+1,tmp+1+top,CMP);

    for(int i(1);i<=top;++i)
        for(int j(i+1);j<=top;++j)
        {
            if(tmp[j].second-tmp[i].second>=dis)
                break;
            
            dis=Hmin(dis,Dist(tmp[i],tmp[j]));
        }
            

    Heriko dis;
}

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;++i)
        scanf("%lf%lf",&a[i].first,&a[i].second);

    std::sort(a+1,a+1+n);
    double ans(Merge(1,n));
    printf("%.4lf",ans);

    Heriko Deltana;
}
posted @ 2022-01-04 17:45  HerikoDeltana  阅读(35)  评论(0编辑  收藏  举报