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

神奇分治,没想到吧
按照x坐标排序。把点分成两部分。
递归统计内部答案
分治主要考虑怎么合并两部分的答案:
首先要距离中间点小于当前ans的点。
然后把这些数按照y排序。
枚举即可。但是我们要记得及时退出。
可以证明对于一个左边的点,右边需要判定的点在三个以内。因为需要保证同侧不会出现更优答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<vector>
#include<set>
#include<iomanip>
#include<ctime>
#include<cstdlib>
#include<cmath>
using namespace std;
#define orz cout<<"lyakioi!!!!!!!!!!!!!!!!!"<<endl
inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;}
int n,tmp[1000001],cnt;
struct node
{
	double x,y;
}a[1000005];
bool cmp(node x,node y)
{
	if(x.x==y.x)return x.y<y.y;
	return x.x<y.x;
}
bool pmc(int x,int y)
{
	return a[x].y<a[y].y;
}
double dfs(int l,int r)//当前区间求出l-r的答案
{
	double ans=1e10;
	if(l>=r)return ans;
	int mid=(l+r)/2;
	ans=min(ans,dfs(l,mid));
	ans=min(ans,dfs(mid+1,r));
	cnt=0;
	for(int i=l;i<=r;i++)
	if(fabs(a[i].x-a[mid].x)<=ans)tmp[++cnt]=i;
	sort(tmp+1,tmp+cnt+1,pmc);
	for(int ii=1;ii<=cnt;ii++)
	for(int jj=ii+1;jj<=cnt;jj++)
	{
		int i=tmp[ii];int j=tmp[jj];
		double dis=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y);
		dis=sqrt(dis);
		ans=min(ans,dis);
//		if(a[j].y-a[i].y>ans)break;
	}
	return ans;
}
int main()
{
	n=r();
	for(int i=1;i<=n;i++)
	{
		a[i].x=r();
		a[i].y=r();
	}
	sort(a+1,a+n+1,cmp);
	printf("%.4lf",dfs(1,n));
}
posted @ 2021-09-03 19:03  lei_yu  阅读(47)  评论(0编辑  收藏  举报