zoukankan      html  css  js  c++  java
  • ZOJ 2017 Quoit Design 经典分治!!! 最近点对问题

    Quoit Design

    Time Limit: 5 Seconds      Memory Limit: 32768 KB

    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

    2
    0 0
    1 1
    2
    1 1
    1 1
    3
    -1.5 0
    0 0
    0 1.5
    0


    Sample Output

    0.71
    0.00
    0.75

    分析:

    先将点按照x排序
    然后可以可以分为两个区间SL,SR,最近点对的分布情况可以分为3个情况
    1.最近点对都在SL
    2.最近点对都在SR
    3.一个在SR,一个在SL或者相反
    1,2种情况都很好解决,直接递归找就可以,假设我们在1,2种情况里面找到了一个最小的距离d,关键就是这第三步,貌似需要n的平方的时间,把左边和右边的两个点都对比一下
    其实不用
    首先,两边的点,与分割线L的距离超过d的,都可以不看了,其次两个点p1,p2(假设p1左,p2右)与分割线L的距离都小于d,如果他们的纵坐标之差大于d
    那么他们也不用看了,这样就是使得可搜索的范围大大减小
    然后最重要的来了!!!
    对左半部分的,与L的记录在d之内的每个p1来说
    右边部分符合上面两个条件的点最多只有6个
    原因就是:d是两个半平面各自内,
    任意两点的最小距离,因此在同一个半平面内,
    任何两点距离都不可能超过d。
    我们又要求P1和P2的水平距离不能超过d,
    垂直距离也不能超过d,
    在这个d*2d的小方块内,最多只能放下6个距离不小于d的点。
    因此,第3步总的比较距离的次数不超过n*6。
    (嗯~~~也不知道自己说的对不对)
    第3步的具体做法是:
    3.1删除所有到L的距离大于d的点。 O(n)
    3.2把右半平面的点按照纵坐标y排序。 O(nlogn)
    3.3对于左半平面内的每个点P1,
    找出右半平面内纵坐标与P1的纵坐标的差在d以内的点P2,
    计算距离取最小值,算出d3。O(n*6) = O(n)
    因为3.2的排序需要O(nlogn),
    所以整个算法的复杂度就是O(n((logn)^2))。
    整体的思路:
    1.把所有的点按照横坐标排序
    2.用一条竖直的线L将所有的点分成两等份
    3.递归算出左半部分的最近两点距离d1,右半部分的最近两点距离d2,取d=min(d1,d2)
    4.算出“一个在左半部分,另一个在右半部分”这样的点对的最短距离d3。
    5.结果=min(d1,d2,d3)
     
    code:
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define max_v 100005
    int n;
    struct node
    {
        double x,y;
    }p[max_v];
    int a[max_v];
    double cmpx(node a,node b)
    {
        return a.x<b.x;
    }
    double cmpy(int a,int b)
    {
        return p[a].y<p[b].y;
    }
    double dis(node a,node b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    double slove(int l,int r)
    {
        if(r==l+1)
            return dis(p[l],p[r]);
        if(l+2==r)
            return min(dis(p[l],p[r]),min(dis(p[l],p[l+1]),dis(p[l+1],p[r])));
        int mid=(l+r)>>1;
        double ans=min(slove(l,mid),slove(mid+1,r));
        int i,j,cnt=0;
        for(i=l;i<=r;i++)
        {
            if(p[i].x>=p[mid].x-ans&&p[i].x<=p[mid].x+ans)
            {
                a[cnt++]=i;
            }
        }
        sort(a,a+cnt,cmpy);
        for(i=0;i<cnt;i++)
        {
            for(j=i+1;j<cnt;j++)
            {
                if(p[a[j]].y-p[a[i]].y>=ans)
                    break;
                ans=min(ans,dis(p[a[i]],p[a[j]]));
            }
        }
        return ans;
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            if(n==0)
                break;
            for(int i=0;i<n;i++)
            {
                scanf("%lf %lf",&p[i].x,&p[i].y);
            }
            sort(p,p+n,cmpx);
            printf("%0.2lf
    ",slove(0,n-1)/2.0);
        }
        return 0;
    }
    /*
    先将点按照x排序
    然后可以可以分为两个区间SL,SR
    最近点对的分布情况可以分为3个情况
    1.最近点对都在SL
    2.最近点对都在SR
    3.一个在SR,一个在SL或者相反
    
    1,2种情况都很好解决
    直接递归找就可以
    假设我们在1,2种情况里面找到了一个最小的距离d
    关键就是这第三步,貌似需要n的平方的时间,把左边和右边的两个点都对比一下
    其实不用
    首先,两边的点,与分割线L的距离超过d的,都可以不看了
    其次两个点p1,p2(假设p1左,p2右)与分割线L的距离都小于d
    如果他们的纵坐标之差大于d
    那么他们也不用看了
    这样就是使得可搜索的范围大大减小
    然后最重要的来了!!!
    对左半部分的,与L的记录在d之内的每个p1来说
    右边部分符合上面两个条件的点最多只有6个
    原因就是:d是两个半平面各自内,
    任意两点的最小距离,因此在同一个半平面内,
    任何两点距离都不可能超过d。
    我们又要求P1和P2的水平距离不能超过d,
    垂直距离也不能超过d,
    在这个d*2d的小方块内,最多只能放下6个距离不小于d的点。
    因此,第3步总的比较距离的次数不超过n*6。
    (嗯~~~也不知道自己说的对不对)
    
    第3步的具体做法是:
    3.1删除所有到L的距离大于d的点。 O(n)
    3.2把右半平面的点按照纵坐标y排序。 O(nlogn)
    3.3对于左半平面内的每个点P1,
    找出右半平面内纵坐标与P1的纵坐标的差在d以内的点P2,
    计算距离取最小值,算出d3。O(n*6) = O(n)
    因为3.2的排序需要O(nlogn),
    所以整个算法的复杂度就是O(n((logn)^2))。
    
    整体的思路:
    1.把所有的点按照横坐标排序
    2.用一条竖直的线L将所有的点分成两等份
    3.递归算出左半部分的最近两点距离d1,右半部分的最近两点距离d2,取d=min(d1,d2)
    4.算出“一个在左半部分,另一个在右半部分”这样的点对的最短距离d3。
    5.结果=min(d1,d2,d3)
    
    */
  • 相关阅读:
    RequestMapping_PathVariable注解
    RequestMapping_Ant 路径
    RequestMapping_请求参数&请求头
    RequestMapping_请求方式
    RequestMapping注解_修饰类
    18)C++对C的增强
    17)C++开始--命名空间
    16)未理解透
    15)文件函数例子
    14)文件操作
  • 原文地址:https://www.cnblogs.com/yinbiao/p/9483764.html
Copyright © 2011-2022 走看看