我们充分发扬人类智慧
平面最近点对
这本来是一道分治
但是智慧法不香吗?
圣经吟唱
在平面最近点对(加强版)里最高赞题解写道:
“我们充分发扬人类智慧:
将所有点全部绕原点旋转同一个角度,然后按 $x$ 坐标排序
根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远
所以我们只取每个点向后的 $5$ 个点来计算答案
这样速度快得飞起,在 $n = 10^6$ 时都可以在 $1$ s 内卡过”
这开启了智慧法过本问题(恶心出题人)的新时代!
问题
装模做样问一问:
给定 $n$ 个二维欧几里得平面上的点 $p_1, p_2, …, p_n$ 请输出距离最近的两个点的距离
思路
由于这是出题人丧心病狂的加强加强版,一般的智慧法被卡,所以,考虑优化智慧法(智慧智慧法):
- 首先,必须旋转坐标系,防止出题人卡我们(但加强加强版的出题人已经疯狂到出 $152$ 组数据了,所以光这样还不行)
- 在时间允许的情况下,能往后多查找几个点就多查找几个点(本题实测 $100$ 卡死)
- 不以 $x$ 排序,而以 $x \times y$ 排序,因为 $x, y$ 应该是等价的(很玄学)
代码
平面最近点对(加强加强版)
( $152$ 个点跑了好久,出题人太丧心病狂了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <bits/stdc++.h> #define LDB long double #define DRG default_random_engine #define UID uniform_int_distribution #define STC static using namespace std; const int N = 4e5 + 5, K = 100; const LDB INF = 2e30 + 0.01; struct Point { LDB p[4]; bool operator < (const Point &t) const { return p[0] * p[1] < t.p[0] * t.p[1]; } } ; int main() { int n; scanf("%d ", &n); DRG e{20051221}; UID<int> u; LDB th = u(e), ans = INF; LDB z = sin(th), w = cos(th); STC Point p[N]; LDB a, b, c, d; for (int i = 1; i <= n; ++i) { scanf("%Lf %Lf", &a, &b); p[i] = {a * w + b * z, -a * z + b * w, a, b}; } stable_sort(p + 1, p + 1 + n); for (int i = 1; i <= n; ++i) { a = p[i].p[2], b = p[i].p[3]; for (int j = 1; j <= K && i + j <= n; ++j) { c = p[i + j].p[2], d = p[i + j].p[3]; ans = min(ans, (a - c) * (a - c) + (b - d) * (b - d)); } } printf("%.0Lf", ans); return 0; }
|