在应用中,常用诸如点、圆等简单的几何对象代表现实世界中的实体。在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息。例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点。这类问题是计算几何学中研究的基本问题之一。下面我们着重考虑平面上的最接近点对问题。
最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。
严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。
首先,对于n^2的枚举,100000的数据量肯定不能通过,于是考虑是否能够在O(nlogn)或O(n)时间复杂度内解决
O(n)肯定不能实现,于是继续考虑nlogn
想到是否能通过分治来解决
一维点对最小距离很好想的,但是如何扩展到二维,这里参考下面博客,其对于平面二维点对的最小距离做法做出了比较严谨的推导和证明
参考博客:https://blog.youkuaiyun.com/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;
}
重要部分代码中已经给出了注释,应该不难理解