hdu 1007 - 平面最近点对 - 分治

版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/82844890

题目链接:点击这里

解题思路:

将坐标按x从小到大排序,现在将原问题大小(l,r)分为两个部分(l,mid),(mid+1,r),记这两区间的最小距离为d1,d2

d = min(d1,d2),显然d不一定是最小的答案,因为可能两个区间各拿去一个点的距离更小.

设从左区间拿出点P1,右区间拿去点P2,显然P1到x == mid的中轴线距离不能超过d,不然不管去找任何P2答案都肯定大于d,P2也是一样的道理.当然P1和P2的纵坐标差也不能超过d.

对于左区间一个P1,右区间满足上述条件的点不会超过4个(很多说是6,更准确的说是3,不放心的去多一点也没事)可以自己画一个2d*d的长方形证明一下.

还有一个结论就是子区间内纵坐标相同的点,横坐标一定差距不小于d.

所以对于纵坐标为y的点来说,满足到中轴线mid的距离小于等于d的点也只有一个,所以对于右区间的P1(x,y),去左区间找前两个纵坐标大于y的点和后两个纵坐标小于y的点(同纵坐标的留下满足条件的那个),如果对右区间在进行排序的话时间复杂度会变为O(n*logn*logn),利用分治来归并排序的话,就可以把一个logn省掉,最后可以达到稳定的O(n*logn)

#include <bits/stdc++.h>
using namespace std;
const int mx = 1e5 + 10;
const double INF = 1.0*1e30;
int n,m;
struct node
{
	double x,y;
	bool operator < (node A)const
	{
		return x < A.x;
	}
}s[mx],q[mx],kep[mx];
double dist(node A,node	B)
{
	return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double divide(int l,int r)
{
	if(l==r) return INF;
    if(l+1 == r){
    	if(s[l].y>s[r].y) swap(s[l],s[r]);
    	return dist(s[l],s[r]);
	}
	int mid = (l+r)>>1,tot = 0;
	int j = mid + 1,head = -1,tail = 0;
	double X = s[mid].x;
	double d = min(divide(l,mid),divide(mid+1,r));
	for(int i=mid+1;i<=r;i++) if(fabs(s[i].x-X)<=d) q[tail++] = s[i];//只留满足条件的一个 
	for(int i=l;i<=mid;i++){
		while(j<=r&&s[j].y<=s[i].y) kep[tot++] = s[j++];//归并 
		if(fabs(s[i].x-X)<=d){
			while(head+1!=tail&&s[i].y>=q[head+1].y) head++;
			for(int k=head;k>=0&&k>head-2;k--) d = min(d,dist(s[i],q[k]));//后两个 
			for(int k=head+1;k<tail&&k<head+3;k++) d = min(d,dist(s[i],q[k])); //前两个 
		}
		kep[tot++] = s[i];
	}
	for(int i=0;i<tot;i++) s[i+l] = kep[i];
	return d;
}
int main()
{
	while(scanf("%d",&n)&&n)
	{
		for(int i=1;i<=n;i++) scanf("%lf%lf",&s[i].x,&s[i].y);
		sort(s+1,s+1+n);
		printf("%.2lf\n",divide(1,n)/2);	
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/82844890
今日推荐