平面内最近点对问题

题目描述

给出同一平面内的 n 个点,求出最近点对的距离

传送门

题解

这里不讨论那种人类精髓做法: 随机旋转

考虑分治
把整个点的序列按 x 先排一遍序
如果要分治,显然是要分开处理某几个部分在合并
所以我们直接考虑二分,按 x 坐标二分,分别求出两部分的最近距离之后,再合并

接下来我们如何合并
在合并过程中,我们还要考虑,可能存在点对正好跨越我们二分出的 mid 的情况
所以我们加一条限制暴力找一下即可

代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define in inline
#define ll long long
#define db double
#define get getchar()
in int read()
{
	int t=0; char ch=get;
	while(ch<'0' || ch>'9') ch=get;
	while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
	return t;
}
const int _=3e5+9; 
int n;
struct yzx{
	db y,x;
}a[_];
in db dis(yzx a,yzx b)
{
	return sqrt(abs(a.x-b.x)*abs(a.x-b.x)+abs(b.y-a.y)*abs(b.y-a.y));
}
in int cmp1(int x,int y)
{
	return a[x].y<a[y].y;
}
int num[_];  
in db work(int l,int r)
{
	db  ans=1000000009;
	if(l==r)
		return ans;
	if(l+1==r)
		return dis(a[l],a[r]);
	int mid=l+r>>1;
	ans=min(work(l,mid),work(mid+1,r));
	int cnt=0;
	for(re int i=l;i<=r;i++)
	if(abs(a[i].x-a[mid].x)<=ans) num[++cnt]=i;
	sort(num+1,num+cnt+1,cmp1);
	for(re int i=1;i<cnt;i++)
		for(re int j=i+1;j<=cnt&&abs(a[num[j]].y-a[num[i]].y)<ans;j++)
			ans=min(ans,dis(a[num[i]],a[num[j]]));
	return ans;
}
in int cmp(yzx x,yzx y) {
	if (x.x == y.x)
        return x.y < y.y;
    else
        return x.x < y.x;
}
int main()
{
	n=read();
	for(re int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read();
	sort(a+1,a+n+1,cmp);
	printf("%.4lf\n",work(1,n));
}
posted @ 2019-10-20 08:53  yzhx  阅读(223)  评论(1编辑  收藏  举报