[题解]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 @ 2024-07-19 10:22  Sinktank  阅读(15)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.