給定平面上 m (3 ≤ m ≤ 100,000) 個點(坐標 0~10000,可能重複),求最遠點對的距離平方。多組測試數據。
幾何結論:最遠點對必定在凸包上。步驟:
O(m log m))O(h),h 為凸包點數)#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define NN 100005
struct Point { int x, y; } a[NN], q[NN];
int top;
// 距離平方
int dis2(Point p1, Point p2) {
return (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y);
}
// 叉積:(p1-p) × (p2-p)
int cross(Point p, Point p1, Point p2) {
return (p1.x-p.x)*(p2.y-p.y) - (p1.y-p.y)*(p2.x-p.x);
}
// 排序:先 x 後 y
int cmp(const void *x, const void *y) {
Point *p1 = (Point*)x, *p2 = (Point*)y;
if (p1->x != p2->x) return p1->x - p2->x;
return p1->y - p2->y;
}
int main() {
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d%d", &a[i].x, &a[i].y);
qsort(a, n, sizeof(Point), cmp);
// Andrew 算法求凸包(去重)
top = 0; q[0] = a[0];
for (int i = 1; i < n; ++i) { // 下凸包
if (a[i].x == q[top].x && a[i].y == q[top].y) continue;
while (top >= 1 && cross(q[top-1], q[top], a[i]) >= 0) --top;
q[++top] = a[i];
}
int t = top;
for (int i = n-1; i >= 0; --i) { // 上凸包
if (a[i].x == q[top].x && a[i].y == q[top].y) continue;
while (top > t && cross(q[top-1], q[top], a[i]) >= 0) --top;
q[++top] = a[i];
}
// 旋轉卡殼求直徑
double ans = 0;
t = 1;
for (int i = 0; i < top; ++i) {
// 移動 t 直到三角形面積不再增大
while (cross(q[i+1], q[t], q[i]) > cross(q[i+1], q[t+1], q[i]))
t = (t + 1) % top;
ans = fmax(ans, fmax(dis2(q[i], q[t]), dis2(q[i+1], q[t+1])));
}
printf("%.0lf\n", ans);
}
return 0;
}
x 升序、y 升序排序q[i] → q[i+1],找到距離最遠的對踵點 q[t]cross(q[i+1], q[t], q[i]) > cross(q[i+1], q[t+1], q[i])(q[i], q[i+1], q[t+1]) 面積 > (q[i], q[i+1], q[t]) 面積t 只單向遞增(模 top),總體 O(h)(q[i], q[t]) 和 (q[i+1], q[t+1])O(m log m)O(m)O(h) ≤ O(m)O(m log m),滿足 m ≤ 100,000建議先完成 NYOJ 78 圈水池(凸包周長),本題為其變體:求凸包直徑而非周長。