[题解]P7883 平面最近点对(加强加强版)——分治解法

P7883 平面最近点对(加强加强版)

解题思路

我们用分治的思路来做。

首先将节点按\(x\)坐标从小到大排序,取中间的节点,把点集划分为\(2\)部分。
接下来我们假设左右\(2\)个部分已经求出答案了。

那么目前的答案就是左右答案的最大值。接下来我们只需要处理两区间相交的部分就可以了。

我们已经知道左右两边的最小值了,设它为\(d\)。那么我们从划分线开始向左右各取一个长度为\(d\)的区间,如下图:

我们要在紫色区域内选\(2\)个点,\(1\)个在划分线左边,\(1\)个在划分线右边。显然只有这样选才可能让答案更优。

我们对于紫色区域内的节点,按\(y\)坐标从小到大排序。

此时我们可以选择暴力枚举每一个节点(因为排过序所以\(j\)一定在\(i\)上面):

for(int i=1;i<cnt;i++){
for(int j=i+1;j<=cnt;j++){
int dis=dist(a[B[i]],a[B[j]]);
if(dis<d) d=dis;
}
}

但显然我们可以进行优化,与\(x\)坐标类似地,我们可以限制\(y_j-y_i<d\),毕竟\(y\)坐标相差超过\(d\)也一定不存在更小答案了嘛。

for(int i=1;i<cnt;i++){
for(int j=i+1;j<=cnt&&a[j].y-a[i].y<d;j++){
int dis=dist(a[B[i]],a[B[j]]);
if(dis<d) d=dis;
}
}

就酱。

总结一下全过程:

  • 我们在主函数中先进行第\(1\)次排序,按\(x\)升序。这是为了能正常按\(x\)坐标分治。
  • 接着在分治的过程中先进行归并排序,按\(y\)升序。
    这一步的原因是我们本来就要对紫色区域进行排序(否则暴力遍历会超时),所以先把该区间内所有节点排序,再筛选出紫色区域内的节点即可。如果不用归并,先筛选出紫色部分的节点的话,相当于分治里套排序,时间复杂度应该就有\(2\)\(\log\)了,而这种做法只有\(1\)\(\log\)
  • 然后从按\(y\)排序后的数组中取出紫色部分的节点存起来,然后对于每一个节点,遍历它上面距离\(<d\)的节点,更新答案\(d\)即可。

坑点:

  • 分割线(就是那个黄线)需要提前存下来,否则归并排序后位置会变。
  • 这道题让输出答案的平方,所以如果你的\(d\)像我一样表示的是平方后的答案。那么注意枚举各种边界都需要用\(\sqrt{d}\),否则否则否则就会像我一样TLE 20pts调了半天调不出来!!!

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 400010
using namespace std;
struct point{
int x,y;
}a[N],tmp[N];
int d=LLONG_MAX,n;
double sqrtd=1e15;
int squadist(point a,point b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void merge(int l,int r){
if(l==r) return;
int mid=(l+r)>>1,pos=1,p1=l,p2=mid+1,midx=a[mid].x;
merge(l,mid);
merge(mid+1,r);
while(p1<=mid&&p2<=r){
if(a[p1].y<a[p2].y) tmp[pos++]=a[p1++];
else tmp[pos++]=a[p2++];
}
while(p1<=mid) tmp[pos++]=a[p1++];
while(p2<=r) tmp[pos++]=a[p2++];
for(int i=l;i<=r;i++) a[i]=tmp[i-l+1];
pos=0;
for(int i=l;i<=r;i++)
if(fabs(a[i].x-midx)<sqrtd) tmp[++pos]=a[i];
for(int i=1;i<pos;i++){
for(int j=i+1;j<=pos&&tmp[j].y-tmp[i].y<sqrtd;j++){
int dis=squadist(tmp[i],tmp[j]);
if(dis<d) d=dis,sqrtd=sqrt(d);
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
sort(a+1,a+1+n,[](point a,point b){return a.x<b.x;});
merge(1,n);
cout<<d<<"\n";
return 0;
}
posted @   Sinktank  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2025-3-6 6:10:58 TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.
点击右上角即可分享
微信分享提示