套圈(最近点对问题)

Problem Description
Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded.
In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it can only encircle one toy at a time. On the other hand, to make the game look more attractive, the ring is designed to have the largest radius. Given a configuration of the field, you are supposed to find the radius of such a ring.

Assume that all the toys are points on a plane. A point is encircled by the ring if the distance between the point and the center of the ring is strictly less than the radius of the ring. If two toys are placed at the same point, the radius of the ring is considered to be 0.
 

Input
The input consists of several test cases. For each case, the first line contains an integer N (2 <= N <= 100,000), the total number of toys in the field. Then N lines follow, each contains a pair of (x, y) which are the coordinates of a toy. The input is terminated by N = 0.
 

Output
For each test case, print in one line the radius of the ring required by the Cyberground manager, accurate up to 2 decimal places. 
 

Sample Input
 
 
20 01 121 11 13-1.5 00 00 1.50
 

Sample Output
 
 
0.710.000.75

 


解法一:O(n^2)暴力枚举

先按x升序排序,如果x相等的话按y升序排序

然后来个二重循环每个点与其他的点计算一下距离。先将min1 min2都置为无限大(INF=0x3f3f3f3f)

每个点算完后的到一个min1,如果S(用距离公式计算的两点距离)大于min1,就可以break了,因为点是排好序的,再往下算还是会更大。得到一个min1再与min2比较一下。这样O(n^2)时间后就得到了答案min2。


#include<cstdio>   
#include<algorithm>   
#include<cmath>   
#define INF 0x3f3f3f3f   
using namespace std;

struct node
{
	double x;
	double y;
}a[100010];

int cmp(node a, node b)
{
	if (a.x == b.x)
		return a.y<b.y;
	return a.x<b.x;
}

double del(node a, node b)
{
	return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}

int main()
{
	int n;
	double min1, min2, s;
	while (scanf("%d", &n) && n)
	{
		for (int i = 0; i<n; i++)
			scanf("%lf %lf", &a[i].x, &a[i].y);
		sort(a, a + n, cmp);
		min2 = INF;
		for (int i = 0; i<n - 1; i++)
		{
			min1 = INF;
			for (int j = i + 1; j<n; j++)
			{
				s = del(a[i], a[j]);
				if (s<min1)
					min1 = s;
				else
					break;
			}
			if (min1<min2)
				min2 = min1;
		}
		printf("%.2lf\n", min2 / 2.0);
	}
	return 0;
}

解法二:分治(nlogn)

参考题解:http://blog.csdn.net/Cxiaokai/article/details/6661005

很厉害的分治方法,学习。

先按照x坐标升序排序,这样得到一组x下标有序的序列。根据序列大小找到中间元素的下标mid,这样把这组序列划分成两组,分别找到这两组的点对中的最小值。用Ans记录两组中的最小值。然后递归下去,把一个组再划分为两个组,按照上面说的找。以下标为mid的元素作为基准,在L到R的区间里找x坐标与基准元素x坐标差小于等于Ans的放入tmp数组中,然后将tmp数组按照y坐标升序排序,在这些点中找最小的距离,然后更新最小距离。



#include<math.h>  
#include<stdio.h>  
#include<iostream>  
#include<algorithm>  
using namespace std;

typedef struct node Point;

struct node
{
	double x;
	double y;
}p[100010];

Point tmp[100010];


int compx(node a, node b)
{
	return a.x < b.x;
}

int compy(node a, node b)
{
	return a.y < b.y;
}

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

double ans(int l, int r)
{
	//if (l == r) return 0;
	if (l + 1 == r)return dis(p[l], p[r]);
	if (l + 2 == r) return min(dis(p[l], p[l + 1]), min(dis(p[l], p[r]), dis(p[l + 1], p[r])));

	int mid = (l + r) >>1;

	double left = ans(l, mid); double right = ans(mid + 1, r);
	double Ans = min(left, right);

	int k = 0;
	for (int i = l; i <= r;i++)
	if (fabs(p[i].x-p[mid].x)<=Ans)
		tmp[k++] = p[i];

	sort(tmp, tmp + k, compy);

	for (int i = 0; i < k;i++)
	for (int j = i + 1; j < k; j++)
	{

		if (tmp[j].y - tmp[i].y >= Ans) break;
			Ans = min(Ans, dis(tmp[i], tmp[j]));
	}

	return Ans;
}

int main()
{
	//freopen("1.txt", "r", stdin);
	int n;

	while (scanf("%d", &n)&&n)
	{
		for (int i = 0; i < n; i++)
			scanf("%lf %lf", &p[i].x, &p[i].y);

		sort(p,p+n,compx);
		printf("%.2f\n", ans(0,n-1)/2);
	}


	return 0;
}

很费解,用了正规的分治方法时间竟然比O(n^2)的慢,是因为数据的原因吗?哈哈哈哈哈哈!



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水之积也不厚,则其负大舟也无力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值