https://www.acwing.com/problem/content/121/
最近点对问题。
分治思想:
将这些点按x值从小到大排序,排序后的下标范围是[l, r]。现在用一个递归函数,求出[l, mid]这些点中的最短距离s1,[mid + 1, r]的最短距离s2。
现在有3种情况:
- 最佳答案在[l, mid]中
- 最佳答案在[mid + 1, r]中
- 最佳答案中的两个点,一个在[l, mid]中,一个在[mid + 1, r]中
前两种都好说,答案就是min(s1, s2)。麻烦的是第三种。
现在我们的目标转换成了在[l, mid]中选一个点,在[mid + 1, r]中选一个点,这两个点是距离最短的、小于s的点。
如果暴力枚举,时间复杂度肯定是不允许的。现在来思考下这两个点在什么范围内。
看左边的点。如果左边的点不在虚线的范围,它到分割线的距离都已经大于s了,所以肯定不存在与之相匹配的点。
右边同理。
因此我们可以把这些点单独放在一个数组中,设为temp。
考虑在虚线内的点2,如果存在与之相匹配的点b,那么b一定位于绿圈之内
将点2移动到分割线处,这时是最坏的情况,此时与之匹配的点的范围可以看作在矩形中
注意到一个事实,那就是分割线右边的点之间,两两距离不可能小于s。那么在这个矩形范围内,能枚举到的点的个数是有限的。最多是6。那么如果我们先按y值排好序,然后再枚举,那么在整个枚举过程中,时间复杂度为O(N)。
为什么最多是6呢。将矩形分割成六个小模块。如果个数大于7,那么至少有两个在同一模块中。在同一模块中的两个点最长距离小于s,与最短距离大于等于s的前提冲突。
当然在实际算法过程中,因为可能枚举到在同一边的点,所以枚举到的个数可能超过6。但是因为在同一边的点之间的距离总是小于s,所以个数是有限的,不会对时间复杂度造成影响。
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int SIZE = 500100;
const double eps = 1e-6, INF = 1e10;
typedef long long LL;
LL T, N;
struct Point {
double x, y;