题目链接:
P1429 平面最近点对(加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1429
思路:
改题使用分治法计算出平面最近点对的距离,可以先将所有的点按照x从小到大排序,接着使用分治法。
分治法:将大问题转化为类似的小问题,解决小问题后通过合并解决大问题。
这里生成了 9 个点作为例子:
按照x坐标排序后依次分别为p1,p7,p3,p2...p6,所以可以把solve(0,9)分成solve(0,4)和solve(4,9):
对于solve(0,4),进一步划分区间:
当红色部分被划分成了绿色部分和红色部分后,可以发现这两个子问题内都只有两个点了。因此直接更新最短距离 像这样依次类推更详细请看链接:
题解 P7883 【平面最近点对(加强加强版)】 - 白色过膝袜 - 洛谷博客 (luogu.com.cn)
现在假设我们已经给 (l,r) 内的点以 y 坐标为关键字排好了序。我们需要依次计算每个点对应的的绿色区域内有哪些点。容易发现,随着 y 坐标的升高,每次只会是绿色区域上面的点加入,只会是绿色区域底部的节点移出。因此也可以使用双指针解决这种问题。
指针 L 和指针 R 之间是需要处理的数据。每次将 L 指针向右移动后,将 R 指针向右移动,直到拓展到需要处理的数据的右边界。容易发现,L 和 R 都只会移动最多 n 次,因此维护待处理数据的时间复杂度是 O(n) 的。
#include<bits/stdc++.h>
#define int long long
#define lmw ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;
const int N=1e6+10;
struct node{
double x,y;
};
double dis(const node a ,const node b){
return (double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cmpx(const node a,const node b) {
return a.x<b.x;
}
bool cmpy(const node a,const node b){
return a.y<b.y;
}
void solve(const vector<node>::iterator l,const vector<node>::iterator r,double &d){
if(r-1<=l) return ;
vector<node> q;
vector<node>::iterator t=l+(r-l)/2;
double w=t->x;
solve(l,t,d);
solve(t,r,d);
inplace_merge(l,t,r,cmpy);
for(vector<node>::iterator x=l;x!=r;++x){
if(abs(w-x->x)<=d) q.push_back(*x);
}
for(vector<node>::iterator x=q.begin(),y=x;x!=q.end();++x){
while(y!=q.end()&&y->y<=x->y+d) ++y;
for(vector<node>::iterator z=x+1;z!=y;++z) d=min(d,dis(*x,*z));
}
}
signed main(){
lmw;
int t;
t=1;
while(t--){
int n;
cin>>n;
double ans=1e18;
vector<node>a;
for(int i=0;i<n;i++){
double x,y;
cin>>x>>y;
a.push_back({x,y});
}
sort(a.begin(),a.end(),cmpx);
solve(a.begin(),a.end(),ans);
cout<<fixed<<setprecision(4)<<sqrt(ans)<<"\n";
}
}