旋转卡壳(rotate吧)

旋转卡壳,我是不会纠结ta到底念什么的,ta应该有2^4种读法
————-lrh

旋转卡壳算法(Rotating Calipers Algorithm):
是解决一些与凸包有关问题的有效算法 就像一对卡壳卡住凸包旋转而得名
一半可以用来O(n)求凸包中两点的最远距离
这里写图片描述
被一对卡壳正好卡住的对应点对称为对踵点(Antipodal point)
这里写图片描述
这里写图片描述
上第一个图是卡壳的一般情况:卡住两点
图二是卡住一条边和一个点
由于实现中,卡住两点的情况不好处理,我们通常关注第二种情况
在第二种情况中,我们可以看到,一个对踵点和对应边之间的距离比其他点要大
也就是一个对踵点和对应边所形成的三角形是最大的
这里写图片描述
可以注意到,A是凸包上离B所在直线最远的点
于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,
计算这个顶点(不一定只计算一个顶点,具体看代码解释)到该边两个端点的距离,并记录最大的值
直观上这是一个O(n^2)的算法,和直接枚举任意两个顶点一样了
但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,
这样就可以不用从头计算最远点,
而可以紧接着上一次的最远点继续计算
于是我们得到了O(n)的算法

看一下代码吧

void KK()
{
    sta[++top]=sta[1];
    int k=1,ans=0;   //k枚举的点编号 
    for (int i=1;i<top;i++)
    {
        while (fabs(Cross(po[sta[i+1]]-po[sta[i]],po[sta[k+1]]-po[sta[i]]))>
              fabs(Cross(po[sta[i+1]]-po[sta[i]],po[sta[k]]-po[sta[i]])))
        {
            k++;
            if (k==top) k=1;    
        }
        ans=max(ans,max(ds(po[sta[k]],po[sta[i]]),ds(po[sta[k+1]],po[sta[i]])));
        ans=max(ans,max(ds(po[sta[k]],po[sta[i+1]]),ds(po[sta[k+1]],po[sta[i+1]])));
    }
    printf("%d",ans);
}

解释一下:

这里写图片描述
保证栈里的是一个环

这里写图片描述
环的操作,让点循环起来

这里写图片描述
while中的判断是把点到直线的距离变成三角形面积的判断
每次我们找出来一对对踵点,就需要计算一下该点和这条边的两个端点的距离,取一个max,
注意Cross是有正有负的,所以前面要加上一个fabs

这里写图片描述
统计答案,i和i+1要分别和k,k+1进行计算,
为了防止k—>k+1和i—>i+1这两条边平行的情况

原文地址:https://www.cnblogs.com/wutongtong3117/p/7673429.html