旋转卡壳2——凸包距离 (POJ2187Beauty Contest 详讲)

POJ2187

一、引言

上节的POJ2187,是求一个凸包中的最远的两个点的距离,用暴力,O(n²)也能过,但是没有用到旋转卡壳。这节就来看看怎么用旋转卡壳来卡这个最远距离。

二、找対踵点

这个最远的距离肯定是在凸包上,这是毋庸置疑的,我们首先建立好凸包后,然后检索凸包的点,寻找这个点的対踵点(即,距离这个点最远的点):用什么方法寻找呢?
这里借助了三角形的面积,具体如下
在这里插入图片描述
首先从检索点0
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
(开始寻找対踵点的时候是从0点的右边一个点即点1开始找的)
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
1.如果:
S(012)-S(011)>0
那么:
対踵点就向下挪一个,即挪到2.
Continue:
2.如果:
S(013)-S(012)>0
那么:
対踵点就向下挪一个,即挪到3.
3.如果:
S(014)-S(013)<0
那么:
対踵点不再向下挪,即退出循环,找到対踵点3,更新最远距离dist(0,3);

接下来检索点1,过程一样,不同的是:
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※这时,対踵点就从刚才停下来的点(即点3)继续找,不需要从1右边一个点开始重新找。
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

在这里插入图片描述

int  solve(Point* Res,int M)
{
    Res[M]=Res[0]; //小技巧,首位连起来,下面加法不会越界
    int High=1;
    double ans=-1;
    for(int i=0;i<M;i++)
    {
        while(DCMP(cross(Res[i],Res[i+1],Res[High+1])-cross(Res[i],Res[i+1],Res[High]))>0)
        {
             High=(High+1)%M;
        }
        ans=max(ans,max(dist(Res[High],Res[i]),dist(Res[High],Res[i+1])));
    }
    return ans;
}

POJ2187完整代码:

/*
POJ2187
by adl
2021-3-27
*/
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<stack>
#define ll long long
#define INF 0x3f3f3f3f
#define EPS 1E-10

using namespace std;
const int maxn=5e4+10;
struct Point{
    double x,y;
    Point(double xx=0,double yy=0):x(xx),y(yy){}
    Point operator+(const Point &p){return Point(x+p.x,y+p.y);}
    Point operator-(const Point &p){return Point(x-p.x,y-p.y);}
    Point operator*(const int k){return Point(k*x,k*y);}
    bool operator<(Point &p)const
    {
        if(x<p.x)   return 1;
        else if(x==p.x)
            return y<=p.y;
        else
            return 0;
    }

    double dot(const Point &p){return x*p.x+y*p.y;}
    double det(const Point &p){return x*p.y-y*p.x;}
};
Point P[maxn],Res[maxn];
inline int DCMP(double x)
{
    if(fabs(x)<EPS)
        return 0;       //注意:这里是返回0,如果差值非常小,就返回0,即假,即:不处理了,等待下次处理。也就是卡精度
    else
        return x<0?-1:1;
}
inline double cross(Point A,Point B,Point C)
{
    return (B-A).det(C-A);
}
double multi(Point A,Point B,Point C)
{
    return (B-A).dot(C-A);
}
inline double dist(Point A,Point B)
{
    return (B-A).dot(B-A);
   // return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}

void Print(Point* P,int N)
{
    cout<<"+++++++++++++++++++++"<<endl;
    for(int i=0;i<N;i++)
        cout<<P[i].x<<"  "<<P[i].y<<endl;
    cout<<"+++++++++++++++++++++"<<endl;
}
inline bool cmp(Point &A,Point &B)
{
    if(cross(P[0],A,B)>0)
        return 1;
    if(cross(P[0],A,B)==0)
        return dist(P[0],A)<dist(P[0],B);
    return 0;


}
int Graham(Point* P,Point*Res,int N)
{
    int init=0;
    for(int i=1;i<N;i++)
    {
        if(P[i].y<P[init].y)
            init=i;
        else if(P[i].y==P[init].y && P[i].x<P[init].x)
            init=i;

    }
    swap(P[init],P[0]);


    sort(P+1,P+N,cmp);
    //Print(P,N);

    int top=0;
    Res[top]=P[0];
    for(int i=1;i<N;i++)
    {
        while(top>0 && cross(Res[top],Res[top-1],P[i])>=0)
            top--;
        Res[++top]=P[i];
    }
    return top+1;
}
int  solve(Point* Res,int M)
{
    //Print(Res,M);
    Res[M]=Res[0];
    int High=1;
    double ans=-1;
    for(int i=0;i<M;i++)
    {
        while(DCMP(cross(Res[i],Res[i+1],Res[High+1])-cross(Res[i],Res[i+1],Res[High]))>0)
        {
             High=(High+1)%M;
        }
        ans=max(ans,max(dist(Res[High],Res[i]),dist(Res[High],Res[i+1])));
    }
    return ans;
}
int main()
{
    int N;
    cin>>N;
    for(int i=0;i<N;i++)
        scanf("%lf%lf",&P[i].x,&P[i].y);
    int M=Graham(P,Res,N);
    printf("%d\n",solve(Res,M));
    return 0;
}

posted @ 2021-03-28 15:44  DuJunlong  阅读(4)  评论(0编辑  收藏  举报  来源