算法实践5 最近点对
问题:
给定平面上\(n\)个点,找出其中的一对点的距离,使得在这\(n\)个点的所有点对中,该距离为所有点对中最小的。
解析
考虑暴力的方法,需要判断所有点对的距离。当\(n\)较小时,比较容易求解。合并两个区间时,可以考虑两个区间的最小值,只有当两个区间的点对距离不超过这个最小值时才会有意义。我们分别考虑\(x\)轴方向和\(y\)轴方向,就能及时终止遍历,达到一个较优的时间复杂度。所以采用分治策略来解决。
设计
double gao(int l, int r) {
处理区间点较少的情况
double ans = min(gao(l, mid), gao(mid + 1, r));
for (i = l -> r)
找出所有p[i].x - p[mid].x <= ans 的点,记总数为tot
按y轴值进行排序
for (i = 1 -> tot)
for (j = i + 1 -> tot)
if (两点y的距离大于ans) break;
更新ans
return ans;
}
分析
因为对\(x\)和\(y\)的大小做了限制,遍历的复杂度并不会太高,在快排存在时可近似看成常数,合并复杂度为\(O(nlogn)\),分治复杂度为\(O(logn)\),总复杂度为\(O(nlog^2n)\)。
源码
https://github.com/Sstee1XD/Algorithm_homework/tree/main/实验5 最近点对
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
const int N = 2e5 + 7;
struct Point {
double x, y;
Point () {}
Point(double x, double y) : x(x), y(y) {}
}p[N], tmp[N];
bool cmp1(Point a, Point b) {
if (a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
bool cmp2(Point a, Point b) {
if (a.y == b.y) return a.x < b.x;
return a.y < b.y;
}
double getDist(Point a, Point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int n;
double gao(int l, int r) {
int num = r - l + 1;
if (num == 1) return 1e50;
if (num == 2) return getDist(p[l], p[r]);
int mid = l + r >> 1;
double ans = min(gao(l, mid), gao(mid + 1, r));
int tot = 0;
for (int i = l; i <= r; ++i) {
if (fabs(p[mid].x - p[i].x) <= ans) tmp[++tot] = p[i];
}
sort(tmp + 1, tmp + 1 + tot, cmp2);
for (int i = 1; i <= tot; ++i) {
for (int j = i + 1; j <= tot; ++j) {
if (tmp[j].y - tmp[i].y > ans) break;
ans = min(ans, getDist(tmp[i], tmp[j]));
}
}
return ans;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i) {
double x, y;
cin >> x >> y;
p[i] = Point(x, y);
}
sort(p + 1, p + 1 + n, cmp1);
cout << gao(1, n) << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(4);
int t = 1;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号