HDU 1007 Quoit Design(最近点对)

来源:互联网 发布:seo网站日志分析工具 编辑:程序博客网 时间:2024/05/20 18:16

Quoit Design

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

【思路分析】
   求一个平面内的最近点对。用到的主要是二分的思想。
   即先将各个点按横坐标从小到大排序,取中点将点集划分为左右两个部分,然后求这两部分的最近点对,并一直二分下去。最后求出左边最近点对的距离为d1,右边最近点对的距离为d2,设δ = min(d1,d2)。但是δ并不一定是最终的结果,因为如果两个点分别位于中点两侧时,它们之间的距离可能比δ还要小,因此还需要进一步确认。

  
   如上图所示(盗图了= =),设中线L将整个S区域分为了SL和SR两个区域,并设δ两区域最近点对距离的最小值,以L为对称轴,δ为宽,向左右做一个长带型区域,则这个区域中的点之间的距离才有可能小于δ。编写的时候直接判段fabs(p[i].x - p[mid].x)的值是否小于δ即可,如果满足,则记录点i的编号位置。




   将满足上述条件的点再按纵坐标从小到大排序组成数组B(注意是放在一起排序,而不是分左右排序,原因见下)。对于上图左边的两个点p、q,则有可能是最近点对。
   进一步讨论,如上图的右图所示,过点p做L的垂线,并做两个以该垂线为公共边的以δ为边长的正方形,则该区域内最多只存在6个点(即两个正方形的6个顶点,因为L右侧区域内两点间最小的距离就为δ了)。因此,对于数组B中的每一个点,只需要判断与其之后的6个点的距离即可。
   为什么是判断之后的六个点而不是另一侧对应的上面三个点和下面三个点呢?这是因为L左边的点与右边的点的距离和L右边的点与左边的点的距离是一样的(说的有些饶舌。。。。)。还是举个具体的例子,见上图的左图,L左边的p点和右边的q点,当计算p点和其右上方q点的距离时,也就相当于计算了q点和其左下方的p点的距离,因此在判断q点时就不需要和其下方的点再进行比较了。这也是为什么把L左右两边的点放在一起排序的原因,如果分开排序的话,那么就是判断左侧区域的点在右侧区域中对应的上面三个点和下面三个点了,但是这就需要去查找对应的这6个点,就算二分的话效率也不如之前的方法高。

代码如下:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <algorithm>using namespace std;#define INF 1e10const int maxn = 100005;int n;double ans;int a[maxn];struct Point{    double x,y;}points[maxn];int cmpX(const Point &a,const Point &b){    return a.x < b.x;}int cmpY(const int &a,const int &b){    return points[a].y < points[b].y;}double distances(int a,int b){    return sqrt((points[a].x - points[b].x) * (points[a].x - points[b].x) + (points[a].y - points[b].y) * (points[a].y - points[b].y));}double closet(int l,int r){    if(l == r)        return INF;    if(l + 1 == r)//两个点        return distances(l,r);    int mid = (l + r) >> 1;    double d1 = closet(l,mid);    double d2 = closet(mid + 1,r);    double d = d1 < d2 ? d1 : d2;    int num = 0;//记录和中间点横坐标相差小于d的点    for(int i = l;i <= r;i++)    {        if(fabs(points[mid].x - points[i].x) < d)        {            a[num] = i;            num++;        }    }    sort(a,a + num,cmpY);//按纵坐标排序    for(int i = 0;i < num - 1;i++)    {        for(int j = i + 1;j <= i + 6 && j < num;j++)        {            if(points[a[j]].y - points[a[i]].y >= d)                break;            d = d < distances(a[i],a[j]) ? d : distances(a[i],a[j]);        }    }    return d;}void init(){    for(int i = 0;i < n;i++)    {        scanf("%lf %lf",&points[i].x,&points[i].y);    }    sort(points,points + n,cmpX);}void solve(){    ans = closet(0,n - 1) / 2;    printf("%.2lf\n",ans);}int main(){    while(scanf("%d",&n) && n != 0)    {        init();        solve();    }    return 0;}



0 0
原创粉丝点击