旋转卡壳

前言

旋转卡壳的模板及用法。

算法讲解

先来了解一下旋转卡壳算法:

旋转卡壳(Rotating Calipers,也称 旋转卡尺)算法,在凸包算法的基础上,通过枚举凸包上某一条边的同时维护其他需要的点,能够在线性时间内求解如凸包直径、最小矩形覆盖等和凸包性质相关的问题。——某个靠谱的 wiki

凸包直径,或着说多边形直径,也可理解成边上任意两点间的距离的最大值。
那么对于一个平面上的一个点集,求解任意两点间距离最大值,最朴素最暴力的办法就是枚举两个点,但是时间复杂度为 $O(n^2)$,不可接受。
那么这里就有一个 $O(n)$ 的妙妙做法:旋转卡壳,因计算时像一对平行线卡住凸包旋转得名。
test
定义如果过凸包上的两个点可以画一对平行直线,使凸包上的所有点都夹在两条平行线之间或落在平行线上,那么这两个点叫做一对对踵点。
那么可知直径是由多边形的平行切线的最远距离决定。那么我们查询对踵点,找到距离最远的那一对即可。
在卡壳旋转的过程中,会出现夹住点和点、点和线(由上图可知,对踵点只有在平行线与凸包某一边平行的时候才会发生变化,那我们就可以找到它变化的时候,去查询对踵点)、线和线,但是我们只用考虑点和线的情况即可。这里其实有一个非常好的性质:随着我们不断按照凸包的顺序逆时针遍历凸包上的点,可知点与定点之间的距离不满足单调性,但是点到定直线的距离满足单调性,所以考虑点和线的情况可以保证正确性与时间复杂度。
一个对踵点到对应边的距离既然是最大的,那我们在确定了一条边时就可以遍历找到它的对应对踵点。这个时候点到直线的距离随着遍历应该是一个单峰函数。那我们找到逆时针的下一个点,如果下一个点到该边的距离比当前点更优,那就跳到下一个点;否则当前点就是该边的对踵点。
判定点到直线距离可以用叉积,遍历复杂度为 $O(n)$,而可以证明,寻找对踵点的复杂度均摊也是 $O(n)$,中途记录对踵点到边的两个顶点的最大距离即可。遍历完即可得到答案。
下面放一下 洛谷旋转卡壳模板

例题

P1452 [USACO03FALL] Beauty Contest G /【模板】旋转卡壳

模板题,代码附在上面。

P4166 [SCOI2007] 最大土地面积

links,一个小应用。
有一个很好的性质:如果凸包有至少四个点,那么组成最大四边形的四个点一定在凸包上。分情况讨论:

凸包大小小于等于 $2$ 时

可知原本所有的点都共线,那么答案为 $0$。

凸包大小为 $3$ 时

那么所有的点都在一个三角形内部,最后的四边形只能为一个凹四边形。
那么我们枚举所有点,那么这种情况下的四边形面积为凸包三角形的面积减去该点与凸包其中一条边构成的小三角形面积(三条边都要考虑一遍),在所有答案中取最大值。

凸包大小大于等于 $4$ 时

根据上文提到的性质(自己证一证吧),四边形四个点都在凸包上。
枚举四边形的对角线,那么在分别对另一条对角线的两个点(分居枚举的对角线两侧)跳指针,找到距离最远的那个点,最后的面积就是对角线长度乘上面的那个点到对角线的距离与下面的点的距离的和,取最大值即可,复杂度 $O(n^2)$。

P3187 [HNOI2007] 最小矩形覆盖

links,也比较典。
性质:最后得到的矩形每条边必定经过凸包的顶点(或者和凸包的一条边重合),可自己证明。
那么根据旋转卡壳调整法,可知在矩形与凸包的一条边重合时更优。
那么枚举边。考虑三个点(该边所对应的):

  1. 最上点,直接用旋转卡壳叉积求面积即可。
  2. 最右点,用点积判断就行了。
  3. 最左点,跟最右点几乎一样,只是旋转方向相反。

那么根据这三个点,做出平行线与垂直线,求交即可的到矩形的顶点。(这个题有点卡精度)

尾声

笔者也只凑的出来这一点了…