平面最近点对
平面最近点对的许多奇奇怪怪的做法
1、正常人的想法
法一:暴力
最直接、最简单的方法,虽然往往拿不到分。
法二:分治法1
考虑把所有的点按x排序。然后你考虑每次把他们分成一半,这样就被拆成了三个部分:
1、左边区间内部
2、右边区间内部
3、跨过了分治的分界线
因为1和2的情况可以被递归解决,所以重点说明3怎么做
你考虑先递归出1和2的答案,设为 $d$ 。
对于跨过分界线的答案,考虑每一个点和分界线的距离,那我们就不需要考虑距离比 $d$ 还要远的点了。
把满足条件的记下来,然后按 $y$ 坐标递增排序。
然后对于每一个点,暴力往后查找最近点对,如果 $y$ 坐标的差都大于 $d$ 了,就可以去统计下一个点的答案了。
注意这个时候也要实时更新 $d$。
时间复杂度 $O(n \log^2 n)$ ,复杂度瓶颈在于分治中间的按y坐标排序。
1 | inline double dis(node x,node y){ |
法三:分治2
考虑优化分治1的瓶颈,发现只要做一个类似归并的过程即可。复杂度降为 $O(n \log n)$
1 | inline double dis(node x,node y){ |
法四:set
这里参考了 OI Wiki 的讲解
我们考虑对于每一个元素,将它和它的左边所有元素的贡献加入到答案中。
把所有点按照 $x$ 为第一关键字、 $y$ 为第二关键字排序,并建立一个以 $y$ 为第一关键字、 $x$ 为第二关键字排序的 multiset。对于每一个位置,我们执行以下操作:
将所有满足 $x_i-x_j\geq d$ 的点从集合中删除。它们不会再对答案有贡献。
对于集合内满足 $ \lvert y_i-y_j \rvert <d $ 的所有点,统计它们和 $p_i$ 的距离。
将 $p_i$ 插入到集合中。
由于每个点最多会被插入和删除一次,所以插入和删除点的时间复杂度为
$O(n \log n)$
代码咕着
2、不正常的人想出来的办法!
声明:以下内容绝大多数都是原创,如果有类似的想法但是提出的比我早,那就是你比我早想出来的,但我确实是自己想的,不是原创的话我会标出来
1、近似排序
我们考虑一个事实,如果我们把所有的点按照 $x*y+x+y$ 排序,那么下标意义上距离这些点很近的点,大多数在真实空间内距离也很近。
感性理解一下
实在不行可以加随机偏移,比如把 $x$ 和 $y$ 全都加上一个1e7之类的东西来减小负数等极端情况带来的影响
然后在暴力比较的时候,根据我们之前的事实,可以只往后比较一些点,不需要比较到头,因为这样大概率不优。
参考代码
1 | struct node{ |
2、随机旋转(非原创)
这个算法是参考了某人的题解
把所有的点随机旋转同一个角度。这个优化的好处是可以冲过一些坐标类hack数据。
1 | mt19937 rd((unsigned long long)new char); |
3、极值点法
这个方法貌似没什么人想到(
具体来说,你考虑找出 $x$ 、 $y$ 、$x*y$ 、 $x^2+y^2$ 从小到大的中间几个点,然后对它们做暴力查找查满。
4、自动优化法
首先随机选取一个起始点,找出包括那个点的答案,然后再把起始点设为答案继续找。如果你发现离 $a$ 的最近点是 $b$ ,离 $b$ 的最近点又是 $a$ ,这时你应该重新随机一个点继续执行算法。
(目前只想到了那么多…我还是太菜了