POJ -3714 Raid

在应用中,常用诸如点、圆等简单的几何对象代表现实世界中的实体。在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息。例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点。这类问题是计算几何学中研究的基本问题之一。下面我们着重考虑平面上的最接近点对问题。

最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。

严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。

首先,对于n^2的枚举,100000的数据量肯定不能通过,于是考虑是否能够在O(nlogn)或O(n)时间复杂度内解决

O(n)肯定不能实现,于是继续考虑nlogn

想到是否能通过分治来解决

一维点对最小距离很好想的,但是如何扩展到二维,这里参考下面博客,其对于平面二维点对的最小距离做法做出了比较严谨的推导和证明

参考博客:https://blog.csdn.net/matricer/article/details/53012797.

知道了这些基础知识,再来解决这道题就方便多了,直接上代码

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int MAX = 1000010;
const double inf = 1e50;
struct node
{
    double x, y;
    bool flag;
};
int n;

inline bool cmp1(node A, node B)
{
    return A.x < B.x;
}

inline bool cmp2(node A, node B)
{
    return A.y < B.y;
}

inline double dist(node A, node B)
{
    return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}

node point[MAX];
node temp[MAX];
double merge(int l, int r)
{
    //边界条件
    //左右相等,两点同性质返回inf
    //两点不同性质返回距离
    if (l == r)
        return inf;
    if (l + 1 == r)
    {
        if (point[l].flag == point[r].flag)
            return inf;
        else
            return dist(point[l], point[r]);
    }
    //分治求解
    int mid = (l + r) >> 1;
    double ll = merge(l, mid);
    double rr = merge(mid + 1, r);
    //求解当前最小dist
    double ans = min(ll, rr);
    //合并中间dist的条件一定为距离边界小于dist的值
    //k记录当前的小于ans的中间值有多少
    int i, j, k = 0;
    for (i = l; i <= r; i++)
    {
        if (fabs(point[i].x - point[mid].x) <= ans)
            temp[++k] = point[i];
    }
    //暴力枚举求解
    sort(temp + 1, temp + k + 1, cmp2);
    for (i = 1; i <= k; i++)
        for (j = i + 1; j <= k; j++)
        {
            //如果当前排序的距离已经大于了当前最小答案,肯定不符合要求
            if (temp[j].y - temp[i].y > ans)
                break;
            //否者直接更新
            if (temp[i].flag != temp[j].flag)
                ans = min(ans, dist(temp[i], temp[j]));
        }
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%lf%lf", &point[i].x, &point[i].y);
            point[i].flag = false;
        }
        for (int i = 1; i <= n; i++)
        {
            scanf("%lf%lf", &point[n + i].x, &point[n + i].y);
            point[n + i].flag = true;
        }
        sort(point + 1, point + 2 * n + 1, cmp1);
        printf("%.3f\n", merge(1, 2 * n));
    }
    return 0;
}

重要部分代码中已经给出了注释,应该不难理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值