题目链接
http://poj.org/problem?id=3714
分析
将点按横坐标排序后分治,求出左半部分和右半部分的答案,根据此答案缩小求跨左右部分的答案的问题规模。
此题是计算几何中经典的“最近点对问题”,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
AC代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
inline int read() {
int num = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return num;
}
const int maxn = 2e5 + 5;
const double inf = 1e20;
struct Node {
int x, y, type;
bool operator < (const Node& rhs) const {
return x < rhs.x;
}
} node[maxn], tmp[maxn];
inline bool comp(const Node& a, const Node& b) {
return a.y < b.y;
}
inline long long pw2(int x) {
return 1ll * x * x;
}
inline double dist(Node a, Node b) {
if (a.type == b.type) return inf;
return sqrt(pw2(a.x - b.x) + pw2(a.y - b.y));
}
double calc(int l, int r) {
if (l == r) return inf;
if (r - l == 1) return dist(node[l], node[r]);
int mid = (l + r) >> 1, tot = 0;
double d = min(calc(l, mid), calc(mid + 1, r));
for (int i = l; i <= r; ++i)
if (abs(node[i].x - node[mid].x) <= d) tmp[++tot] = node[i];
sort(tmp + 1, tmp + tot + 1, comp);
for (int i = 1; i < tot; ++i)
for (int j = i + 1; j <= tot; ++j) {
if (tmp[j].y - tmp[i].y <= d)
d = min(d, dist(node[i], node[j]));
else break;
}
return d;
}
int main() {
int t = read();
while (t--) {
int n = read();
for (int i = 1; i <= n; ++i)
node[i].x = read(), node[i].y = read(), node[i].type = 0;
for (int i = n + 1; i <= 2 * n; ++i)
node[i].x = read(), node[i].y = read(), node[i].type = 1;
sort(node + 1, node + 2 * n + 1);
printf("%.3f\n", calc(1, 2 * n));
}
return 0;
}