目录
问题描述
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.
你曾在操场上玩过投圈游戏吗?投圈游戏是在一些玩具上投掷扁平的圆环,所有被圆环环绕的玩具都会被奖励。在Cyberground领域,每个玩具的位置都是固定的,圆环被精心设计,一次只能环绕一个玩具。另一方面,为了使游戏看起来更具吸引力,圆环被设计成具有最大的半径。给定一个场地的配置,你应该找到这样一个圆环的半径。假设所有玩具都是平面上的点。如果一个点与圆环中心的距离严格小于圆环的半径,则该点被圆环环绕。如果两个玩具被放置在同一点上,则圆环的半径被视为0。
输入 输入由多个测试用例组成。对于每个用例,第一行包含一个整数N(2 <= N <= 100,000),即该字段中玩具的总数。然后是N行,每行包含一对(x,y),即玩具的坐标。输入以N = 0结束。
输出 对于每个测试用例,在一行中打印Cyberground管理器要求的环半径,精确到2位小数。
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
问题分析
解题关键在于题目中的这句话:“一次只能环绕一个玩具”
也就是说,我们需要找到距离最近的两个玩具,且保证不同时套中它们两个,此时意味着圆环直径最大为这两个玩具的距离。
也就是把问题变成了从一堆点中找到距离最近的两个点的问题。
问题解决
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#define N 101000
using namespace std;
struct site //玩具点信息
{
double x; //横纵坐标
double y;
};
site toy[N]; //总的玩具结构体数组
site choose[N]; //后面做比较用的中间部分的玩具结构体数组
bool cmp_x(site p1,site p2) //排序依据函数
{
return p1.x<p2.x; //x从小到大
}
bool cmp_y(site p1,site p2)
{
return p1.y<p2.y; //y从小到大
}
double distance(site p1,site p2) //计算两点间距离
{
double dx=abs(p1.x-p2.x);
double dy=abs(p1.y-p2.y);
double dd=sqrt(dx*dx+dy*dy); //注意直接乘,用pow会超时
return dd;
}
double least(int b,int e) //求最短两点间距离
{
if(e-b<=2) //递归出口
{
if(e-b==1) //只剩两个点,直接求
{
return distance(toy[b],toy[e]);
}
else if(e-b==2) //剩三个点,直接算、比较
{
double d1=distance(toy[b],toy[b+1]);
double d2=min(d1,distance(toy[b],toy[e]));
double d3=min(d2,distance(toy[b+1],toy[e]));
return d3;
}
}
else //点数较多需要递归求解
{
int mid=(b+e)/2; //求出中点下标
double dl=least(b,mid); //左边递归求最小两点间距离
double dr=least(mid+1,e); //右边递归求最小两点间距离
double d=min(dl,dr); //左右最小距离再比较取最小
//接下来看中间部分是否有比左右更小的
int count=0; //计数器
for(int i=b;i<=e;i++)
{
if(abs(toy[mid].x-toy[i].x)<d) //若某点与中点玩具横坐标间距小于d,
{ //有机会创造更小距离,加入choose数组等待后续比较
choose[count]=toy[i];
count++;
}
}
sort(choose,choose+count,cmp_y); //将加入choose数组的玩具点再按纵坐标排序
for(int i=0;i<count;i++)
{
for(int j=i+1;j<count;j++)
{
if(choose[j].y-choose[i].y>=d)
{ //若前后纵坐标之差>=d,则新距离肯定不会比d更小,直接舍弃不算
break;
}
d=min(d,distance(choose[i],choose[j])); //否则计算,并尝试更新最小距离
}
}
return d; //返回最小距离
}
}
int main()
{
int T;
cin>>T;
getchar();
while(T!=0)
{
for(int i=0;i<T;i++)
{
scanf("%lf %lf",&toy[i].x,&toy[i].y);
}
sort(toy,toy+T,cmp_x); //先按横坐标排序
double d=least(0,T-1);
printf("%.2lf\n",d/2); //圆环最大半径即为最小距离的一半
cin>>T;
getchar();
}
return 0;
}