旋转卡壳2——凸包距离 (POJ2187Beauty Contest 详讲)
一、引言
上节的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;
}