<算法学习>分治——求最近两点间距离

目录

问题描述

问题分析

问题解决

参考博客


问题描述

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. 4↵
  2. 0 3↵
  3. 3 2↵
  4. 4 0↵
  5. 7 1↵
  6. 0↵
以文本方式显示
  1. 1.12↵
1秒64M0

问题分析

解题关键在于题目中的这句话:“一次只能环绕一个玩具

也就是说,我们需要找到距离最近的两个玩具,且保证不同时套中它们两个,此时意味着圆环直径最大为这两个玩具的距离。

也就是把问题变成了从一堆点中找到距离最近的两个点的问题。

问题解决

#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;
}

参考博客

递归与分治 | 2:最接近点对问题 —— 例题:套圈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值