HDU 1007 [Quoit Design] 分治

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007

题目大意:N个点,求最近一对点的距离的一半。

关键思想:最傻瓜式的枚举所有点对的距离找最短 O(n^2),会TLE。

分治可以优化成O(nlogn)。二分区间,考虑三种情形:点对的两点都在左区间、两点都在右区间、两点一左一右

前两种情况,可以递归地解出来,分别为d1,d2。第三种,可以依据min(d1,d2)收缩成一条带状区域(勾股定理显然)。

然后对带中所有点进行处理,此时又可依据min(d1,d2)优化。可以证明,在x方向为2d,y方向为d的矩形区域内,最多有7个点,否则最小距离一定大于d。

代码如下:

//分治 参数要确认是否为闭区间 
#include <iostream>
#include <algorithm>
#include <vector>
#include <math.h>
using namespace std;

int N;

struct point{
	double x,y;
}egx[100010],egy[100010];

bool cmpx(point x,point y){
	return x.x<y.x;
}

bool cmpy(point x,point y){
	return x.y<y.y;
}

double dis(point x,point y){
	return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}

double Clodis(int l,int r){
	if(l+1==r)return dis(egx[l],egx[r]);//只有两个或三个点,直接穷举 
	if(l+2==r)return min(dis(egx[l],egx[r]),min(dis(egx[l],egx[l+1]),dis(egx[l+1],egx[r])));
	
	int cnt=0,mid=l+r >> 1;
	double d=min(Clodis(l,mid),Clodis(mid+1,r));	//递归的过程解 
	for(int i=l;i<=r;i++)
		if(fabs(egx[i].x-egx[mid].x)<=d)egy[cnt++]=egx[i];	//带状区域内的点放入egy数组 
	sort(egy,egy+cnt,cmpy);	//对y进行排序 ,以后只需顺次遍历少数点 
	for(int i=0;i<cnt;i++){
		//两种遍历方式都可以 
//		for(int j=i+1;j<cnt;j++){
//			if(egy[j].y-egy[i].y>=d)break;
//			d=min(dis(egy[i],egy[j]),d);
//		}
		for(int j=1;i+j<cnt&&j<8;j++){
			if(i!=j)
				d=min(d,dis(egy[i],egy[j]));
		}
	}
	return d;
}

int main(){
	double max=1e19;
	while(~scanf("%d",&N)&&N){
		max=1e19;
		for(int i=0;i<N;i++)
			scanf("%lf%lf",&egx[i].x,&egx[i].y);
		sort(egx,egx+N,cmpx);
		printf("%.2lf\n",Clodis(0,N-1)/2);
	}
	return 0;
}

  

posted @ 2017-02-04 20:16  哇咔咔咔  阅读(113)  评论(0编辑  收藏  举报