zsc1567: 烦恼的小明&hdu 1007(分治)


1567: 烦恼的小明

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 115   Solved: 6
[ Submit][ Status][ Web Board]

Description

A市政府2015年拿到了一笔教育资金,于是想建一所中学和一个图书馆。作为此项工程的策划工程师,小明要在A市n块空地上选两块来建中学和图书馆。曾经也是一个学霸的小明,深知图书馆对一个学霸来说是多么重要的地方,于是小明想在这n块空地上找两块来建中学和图书馆,使得它们的距离最近,算出大概需要步行多久,步行速度约为1.5m/s。可是此项工程还有许多别的难题要解决,于是小明寻求聪明的你来帮他解决这个问题。

Input

输入有多组数据。每一组数据第一行输入n(2<=n<=100000),表示有n块空地。接下来n行,每行为每一块空地的坐标x和y(x和y均为double型,-1000.0<=x,y<=1000.0)。注意:可能出现n块地中有重复坐标的,中学和图书馆允许在同一坐标(当该坐标出现2次及以上时)。当n为0时结束。

Output

每一组数据输出步行所需要的时间(s),保留两位小数。

Sample Input

2
0 0
2 2
3
1.6 -1
5 7
-1 9
3
0 0
0 0
1 1
0

Sample Output

1.89
4.22
0.00



这个题目其实就是hdu1007的原题目,这里涉及到了一个叫做最近点对的算法,这题暴力很明显是会TLE的,所以要优化。而这里的优化可以用分治法胡乱一波。其实这个算法就是说在x轴中排好序后,模拟一棵树,就是分治。不断缩小范围,直到剩下两个或三个节点就返回(代码有解释),之后再存起在【mid-ans,mid+ans】的节点,然后再在y轴排序。之后逐个求距离。之所以这种方法可行是因为ans的缘故,将范围收窄在了mid的左右。
参考资料:
http://blog.csdn.net/hackbuteer1/article/details/7482232
http://blog.csdn.net/liufeng_king/article/details/8484284

AC代码:


#include<iostream>
#include<functional>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
#define CRL(a) memset(a,0,sizeof(a))
#define QWQ ios::sync_with_stdio(0)
typedef unsigned long long LL;
typedef  long long ll;

const int T = 100000+50;
const int mod = 1000000007;

struct node
{
	double x,y;
}a[T],b[T];

bool cmp(const node& a,const node& b){    return a.x<b.x; }
bool Cmp(const node& a,const node& b){    return a.y<b.y; }

double dis(const node& a,const node& b)//欧几里得距离
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}


double DFS(int L,int R)//分治
{
	if(L+1==R)//两个点时
		return dis(a[L],a[R]);
	if(L+2==R)//当分到只剩三个点时,不能再分了,不然有一边不能组成线段
		return min(dis(a[L],a[R]),min(dis(a[L],a[L+1]),dis(a[L+1],a[R])));
	int mid = (L+R)>>1;
	double ans = min(DFS(L,mid),DFS(mid+1,R));

	int c=0;
	for(int i=L;i<=R;++i){
		if(a[i].x>=a[mid].x-ans&&a[i].x<=a[mid].x+ans)
		//找出在【-ans,ans】的范围内的点才有可能组合出比ans小的线段
		b[c++] = a[i];
	}

	sort(b,b+c,Cmp);
	for(int i=0;i<c;++i){
		for(int j=i+1;j<c;++j){
			if(b[j].y-b[i].y>=ans)break;
			//只要是距离大于ans就直接退出,因为已经找不到更小的线段了
			ans =min(ans,dis(b[i],b[j]));
		}
	}
	return ans;
}

int main()
{

#ifdef zsc
    freopen("input.txt","r",stdin);
#endif

    int i,j,k,n,t;

    while(scanf("%d",&n),n)
    {
		for(i=0;i<n;++i){
			scanf("%lf%lf",&a[i].x,&a[i].y);
		}
		sort(a,a+n,cmp);
		printf("%.2lf\n",DFS(0,n-1)/1.5);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值