P1429 平面最近点对
作者:互联网
感谢所有AC
传送门
思路
分治能解决这个题目吗?乍一看并不可行,如果把所有点中分,取左部分的最小距离和右部分的最小距离来作为所有点的最小距离,这样的分治策略明显是不正确的,因为这样就忽视了左部分和右部分中的点形成的点对,即一些跨界的距离可能是最短距离。这是分治所遇到的问题,如果能够解决这个问题,那么分治将是一个很好的解法,时间上也是很可观的。
怎么处理跨界点对?不妨把思路放简单一点,我直接把所有跨界点对的距离求出来,然后用这些距离来更新答案不就ok了?这样的时间复杂度是 n2 的,是否有优化的空间?由于答案出自三个部分,左区间点对,右区间点对,跨界点对。如果在先知道了左右区间点对的最短距离后,似乎可以用剪枝的思想来优化跨界点对的求解。
假如目前左右区间点对最优解为 len ,那么只要左区间内的点对跟中分线的垂直距离大于等于 len ,那么他就不可能是最优解,右区间同理。这样就筛除了很大数量的点,接下来,把可能是最优解的点存进数组中,然后 n2 地逐个计算距离并更新答案。这样就可以实现最优解的实现了。
代码
#include <iostream>
#include<algorithm>
#include<cmath>
#define maxn 200007
using namespace std;
struct node {
double x, y;
bool operator<(const node& A) {
if (x != A.x)return x < A.x;
return y < A.y;
}
}point[maxn];
int temp[maxn << 1], n;
double dis(node a, node b)
{
double x = (a.x - b.x) * (a.x - b.x);
double y = (a.y - b.y) * (a.y - b.y);
return sqrt(x + y);
}
double daq(int l, int r)
{
if (l == r)
return 1e9;
//由于是计算点对距离,需要如下的特判来提供距离
if (l + 1 == r)
return dis(point[l], point[r]);
int m = l + r >> 1, k = 0;
//先找出左右区间点对最优解
double d = min(daq(l, m), daq(m + 1, r));
//第一次剪枝
for (int i = l; i <= r; i++)
if (fabs(point[i].x - point[m].x) < d)
temp[++k] = i;
//计算点对距离时,再次剪枝优化复杂度
for (int i = 1; i < k; i++)
for (int j = i + 1; j <= k && fabs(point[temp[i]].x - point[temp[j]].x) < d; j++)
d = min(d, dis(point[temp[i]], point[temp[j]]));
return d;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> point[i].x >> point[i].y;
sort(point + 1, point + n + 1);
printf("%.4lf", daq(1, n));
return 0;
}
标签:跨界点,point,距离,最近,平面,区间,P1429,最优,include 来源: https://www.cnblogs.com/xqk0225/p/16147282.html