初见 | 平面最近点对
闲来无事,学个新东西。
问题概述
求有 \(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;
}
Do you like WHAT YOU SEE ?