[题解]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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效