【计算几何】旋转卡壳

计算几何-旋转卡壳

读法

(其实我也不知道该怎么读,有16种读法)

$ xu\grave{a}n $ $ zhu\grave{a}n $ $ qi\check{a} $ $ qi\grave{a}o $

$ \huge{旋转卡壳}$

凸多边形的切线

如果一条直线与凸多边形有交点,并且整个凸多边形都在这条直线的一侧,那么这条直线就是该凸多边形的一条切线。

对踵点

如果过凸多边形上两点作一对平行线,使得整个多边形都在这两条线之间,那么这两个点被称为一对对踵点。

凸多边形的直径

即凸多边形上任意两个点之间距离的最大值。直径一定会在对踵点中产生,如果两个点不是对踵点,那么两个点中一定可以让一个点向另一个点的对踵点方向移动使得距离更大。并且点与点之间的距离可以体现为线与线之间的距离,在非对踵点之间构造平行线,一定没有在对踵点构造平行线优,这一点可以通过平移看出。

旋转卡壳

先上一张比较标致的图,来对旋转卡壳有一个初步的了解:

对于实现旋转卡壳,我们首先可以先去求出凸包,然后去枚举每一条边,去找对踵点,即可找出凸包直径。时间复杂度\(O(n \log n)\)

在旋转的时候用叉积对应的面积,来找对踵点就好了,因为在凸包上有一个单调性,在找对踵点的时候,实际上是\(O(n)\)的,建议好好思考一下。

\(\color{red} {注意,在旋转时为一个环,需要注意边界问题,要不然会收获血与泪的教训}\) (别问我怎么知道的)

【模板】旋转卡壳

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
struct geometric{
    double x,y,dss;
    friend geometric operator + (const geometric a,const geometric b){return (geometric){a.x+b.x,a.y+b.y};} 
    friend geometric operator - (const geometric a,const geometric b){return (geometric){a.x-b.x,a.y-b.y};} 
    double dis(geometric a,geometric b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
    double dot(geometric a1,geometric a2,geometric b1,geometric b2){return (a2.x-a1.x)*(b2.x-b1.x)+(a2.y-a1.y)*(b2.y-b1.y);}
    double cross(geometric a1,geometric a2,geometric b1,geometric b2){return (a2.x-a1.x)*(b2.y-b1.y)-(a2.y-a1.y)*(b2.x-b1.x);}
    double corner(geometric a1,geometric a2,geometric b1,geometric b2){return dot(a1,a1,b1,b2)/(dis(a1,a2)*dis(b1,b2));}
};
int n,top;double ans;
geometric d[maxn],opt,st[maxn];
bool cmp(geometric a,geometric b)
{
    double tamp=opt.cross(d[1],a,d[1],b);
    if(tamp>0)return true;
    if(tamp==0&&opt.dis(d[1],a)<opt.dis(d[1],b))return true;
    return false;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&d[i].x,&d[i].y);
        if(i!=1&&d[i].y<d[1].y)
        {
            swap(d[1].y,d[i].y);
            swap(d[1].x,d[i].x);
        }
        if(i!=1&&d[i].y==d[1].y&&d[i].x>d[1].x)
            swap(d[1].x,d[i].x);
    }
    sort(d+2,d+n+1,cmp);
    st[++top]=d[1];
    for(int i=2;i<=n;i++)
    {
        while(top>1&&opt.cross(st[top-1],st[top],st[top],d[i])<=0)top--;
        st[++top]=d[i];
    }
    st[++top]=d[1];
    for(int i=2,j=3;i<=top;i++)
    {
        while(opt.cross(st[i-1],st[i],st[i-1],st[j])<opt.cross(st[i-1],st[i],st[i-1],st[j+1]))j==top-1?j=1:j++;// !!!!
        ans=max(ans,max(opt.dis(st[i],st[j]),opt.dis(st[i-1],st[j])));
    }
    printf("%d",int(ans*ans));
    return 0;
}

一些例题

[HNOI2007]最小矩形覆盖

[SCOI2007]最大土地面积

[HNOI2008]水平可见直线

[ZJOI2008]瞭望塔

Smallest Bounding Rectangle

部分资料参考自洛谷日报

posted @ 2022-05-19 22:44  Jekyll_Y  阅读(438)  评论(0编辑  收藏  举报