1. 程式人生 > 其它 >P1429 平面最近點對(加強版)

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));
}