旋转卡壳(入门)

旋(xuán)转(zhuàn)卡(qia)壳(qiào)

旋转卡壳可以用于求凸包的直径、宽度,两个不相交凸包间的最大距离和最小距离等。虽然算法的思想不难理解,但是实现起来真的很容易让人“卡壳”。


其实简单来说就是用一对平行线“卡”住凸包进行旋转。
被一对卡壳正好卡住的对应点对称为对踵点,对锺点的具体定义不好说,不过从图上还是比较好理解的。
可以证明对踵点的个数不超过3N/2个 也就是说对踵点的个数是O(N)
对踵点的个数也是我们下面解决问题时间复杂度的保证。

卡壳呢,具体来说有两种情况:
1.

一种是这样,两个平行线正好卡着两个点;
2.

一种是这样,分别卡着一条边和一个点。
而第二种情况在实现中比较容易处理,这里就只研究第二种情况。

在第二种情况中 我们可以看到 一个对踵点和对应边之间的距离比其他点要大(借用某大神的图··)

也就是一个对踵点和对应边所形成的三角形是最大的 下面我们会据此得到对踵点的简化求法。


下面给出一个官方的说明:
Compute the polygon's extreme points in the y direction. Call them ymin and ymax. Construct two horizontal lines of support through ymin and ymax. Since this is already an anti-podal pair, compute the distance, and keep as maximum. Rotate the lines until one is flush with an edge of the polygon. A new anti-podal pair is determined. Compute the new distance, compare to old maximum, and update if necessary. Repeat steps 3 and 4 until the anti-podal pair considered is (ymin,ymax) again. Output the pair(s) determining the maximum as the diameter pair(s).

要是真的按这个实现起来就麻烦到吐了。。
根据上面的第二种情况,我们可以得到下面的方法:
如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。
直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。
然而我们可以发现 凸包上的点依次与对应边产生的距离成单峰函数(如下图:)


这个性质就很重要啦。

根据这个凸包的特性,我们注意到逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算。于是我们得到了O(n)的算法。这就是所谓的“旋转”吧!
利用旋转卡壳,我们可以在O(n)的时间内得到凸包的对锺点中的长度最长的点对。

又由于最远点对必然属于对踵点对集合 ,那么我们先求出凸包 然后求出对踵点对集合 然后选出距离最大的即可

那么具体的代码就很容易实现了,利用叉积,代码只有这么几行的长度:

double rotateCalipers(Point *ch,int n)
{
    double ans=-INF;
    ch[n]=ch[0];
    int q=1;
    for(int i=0;i<n;i++)//枚举边(i,i+1)
    {
        //寻找面积最大的三角形
        while(cross(ch[q]-ch[i+1],ch[i]-ch[i+1])<cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1]))
        {
           q=(q+1)%n;
        }
        ans=max(ans,max(length(ch[q]-ch[i]),length(ch[q+1]-ch[i+1])));
    }
    return ans;
}

对多边形进行旋转卡壳,多边形的边只能有两个点,即不存在三点共线,所以求凸包的时候要注意,不要把共线的点也求进来

其中cross()是计算叉积,因为凸包上距离一条边最远的点和这条边的两个端点构成的三角形面积是最大的。之所以既要更新(ch[q],ch[i])又要更新(ch[q+1],ch[i+1])是为了处理凸包上两条边平行的特殊情况。
ps:为什么是边( q, i )和边( q+1, i+1 ),而不是边( q, i+1 )和边( q+1, i ),动手画画图就知道了

Beauty Contest
题意:
求凸多边形直径(最远距离的两点的距离)
题解:
当然是用旋转卡壳啦

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=50010;
const int INF=0x7fffffff;
struct Point
{
    double x,y;
    Point (double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Point in[MAXN],out[MAXN];
Vector operator - (Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
bool operator <(const Point &a,const Point &b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int convexHull(Point *p,int n,Point *ch)
{
    sort(p,p+n);
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--)
    {
        while(m>k&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
double length(Vector A)
{
    return A.x*A.x+A.y*A.y;
}
double rotateCalipers(Point *ch,int n)
{
    double ans=-INF;
    ch[n]=ch[0];
    int q=1;
    for(int i=0;i<n;i++)//枚举边(i,i+1)
    {
        //寻找面积最大的三角形
        while(cross(ch[q]-ch[i+1],ch[i]-ch[i+1])<cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1]))
        {
           q=(q+1)%n;
        }
        ans=max(ans,max(length(ch[q]-ch[i]),length(ch[q+1]-ch[i+1])));
    }
    return ans;
}
int main()
{
    int n,ans;
    double res,len;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&in[i].x,&in[i].y);
        }
        ans=convexHull(in,n,out);
        printf("%.f\n",rotateCalipers(out,ans));
    }
    return 0;
}

其实没学旋转恰壳时,我是先求出凸包,然后再枚举任意凸包两点距离暴力过的,
O( n^2)居然不超时

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=50010;
const int INF=0x7fffffff;
struct Point
{
    double x,y;
    Point (double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Point in[MAXN],out[MAXN];
Vector operator - (Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
double cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
bool operator <(const Point &a,const Point &b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int convexHull(Point *p,int n,Point *ch)
{
    sort(p,p+n);
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--)
    {
        while(m>k&&cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
double length(Vector A)
{
    return A.x*A.x+A.y*A.y;
}
int main()
{
    int n,ans;
    double res,len;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&in[i].x,&in[i].y);
        }
        ans=convexHull(in,n,out);
        res=-INF;
        for(int i=0;i<ans;i++)//枚举两点距离
        {
            for(int j=i+1;j<ans;j++)
            {
                len=length(out[j]-out[i]);
                if(res<len) res=len;
            }
        }
        printf("%.f\n",res);
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容