POj2187 【模板】旋转卡壳 / 选美大赛
题目描述
农夫约翰奖的牛贝西(Bessie)刚刚在牛选美比赛中获得第一名,并获得了“牛世界小姐”的头衔。结果,贝茜将参观世界各地的N个农场(2个== N <= 50,000个),以便在农民及其母牛之间传播商誉。为简单起见,世界将表示为二维平面,其中每个场都位于一对整数坐标(x,y)上,每个坐标的值都在-10,000 … 10,000范围内。没有两个场共享同一对坐标。
即使Bessie在成对的农场之间直接成一直线行驶,但一些农场之间的距离仍可能很大,因此她希望随身携带一个装满干草的手提箱,以便旅途中的每条腿都有足够的食物吃。由于Bessie会在她访问的每个农场都给手提箱装满行李,因此她想确定自己可能需要旅行的最大距离,以便她知道自己必须携带的手提箱的大小。通过计算所有成对农场之间的最大距离来帮助Bessie
输入输出格式
输入格式
第1行:一个整数,N
第2.行。N+ 1:两个以空格分隔的整数x和y,用于指定每个场的坐标
输出格式
第1行:单个整数,是彼此最远的一对场之间的平方距离。
输入输出样例
输入#1
4
0 0
0 1
1 1
1 0
输出#1
2
说明
场1(0,0)和场3(1,1)的距离最长(2的平方根)
分析

旋转卡壳:
逆向思考,如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。这其实也是在寻找在这条边的方向上距离最远的两个点,我们称这样两个点为对踵点对。直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算,于是我们得到了O(n)的算法。而计算距离时我们发现边是不变的,因此距离实际上就是三角形的高,所以可以使用叉积计算三角形面积,由于底边相同,三角形面积的大小情况就是距离大小情况。
我的代码
#include<bits/stdc++.h>
using namespace std;
#define Maxn 50010
#define INF 1000000000
int re(){
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-'){t=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*t;
}
struct node{
int x,y;
}p[Maxn],s[Maxn];
int top,n,mi;
double jiao(node aa,node bb,node cc){
return (aa.x-cc.x)*(bb.y-cc.y)-(aa.y-cc.y)*(bb.x-cc.x);
}
double ju(node aa,node bb,node cc){
return ((aa.x-cc.x)*(aa.x-cc.x)+(aa.y-cc.y)*(aa.y-cc.y))-((bb.x-cc.x)*(bb.x-cc.x)+(bb.y-cc.y)*(bb.y-cc.y));
}
bool cmp(node aa,node bb){
if(jiao(aa,bb,p[0])>0)
return 1;
if(jiao(aa,bb,p[0])==0&&ju(aa,bb,p[0])<=0)
return 1;
return 0;
}
long long disc(node a,node b){
return 1LL*(a.x-b.x)*(a.x-b.x)+1LL*(a.y-b.y)*(a.y-b.y);
}
void Find(){
for(int i=0;i<n;i++){
if(p[i].y<p[mi].y ||(p[i].y==p[mi].y&&p[i].x<p[mi].x))
mi=i;
}
node lin=p[mi];
p[mi]=p[0];
p[0]=lin;
sort(p+1,p+n,cmp);
s[0]=p[0];
s[1]=p[1];
top=1;
for(int i=2;i<n;i++){
while(top>1&&jiao(s[top-1],s[i],s[top])>0)
top--;
s[++top]=p[i];
}
}
long long getit(){
long long re=0;
if(top==1)
return disc(s[0],s[1]);
s[++top]=s[0];
int j=2;
for(int i=0;i<top;i++){
while(jiao(s[i],s[i+1],s[j])<jiao(s[i],s[i+1],s[j+1]))
j=(j+1)%top;
re=max(re,max(disc(s[i],s[j]),disc(s[i+1],s[j])));
}
return re;
}
int main(){
n=re();
for(int i=0;i<n;i++){
p[i].x=re();
p[i].y=re();
}
long long ans=INF;
Find();//寻找凸包,详细解释请看 https://blog.youkuaiyun.com/MIT_mit/article/details/103736296
ans=getit();//求出凸包内两点最长距离
printf("%lld",ans);
return 0;
}
太丑的话见谅见谅。