题目大意:
给出n个点,求出最远的两个点之间的距离
分析:
第一想法:暴力肯定TLE啊( ⊙ o ⊙ )!,可是5W个点捏!
然后旁边的童鞋查了题解,我就凑过去看了几眼,旋转卡壳??SMG??
我们还是先认识一下凸包吧:
顾名思义,就是凸的包((⊙﹏⊙)b等于没说,还是看图吧),由图可知,平面上最远点对一定存在于凸包上。什么??严谨的证明??反证法好了…………>_<
然后就有了一个严肃的问题:怎么求凸包捏??
Graham_scan算法闪亮登场✧(≖ ◡ ≖✿)!!!
我们先找一个一定存在于凸包上的点作为基点,一般都找纵坐标最小的点吧………..那么就叫基点为B吧……
然后将平面上的点S按照S与B形成的向量与X轴的夹角从小到大拍一遍序,然后依次考虑这些点…………
(⊙v⊙)嗯,建一个栈,维护凸包,我们要求凸包上每条相邻的线段旋转方向都一致,并且与扫描的方向相反>_<
怎么判断呢??叉积,(⊙o⊙)…这是SMG??
有这样两个向量叫做X和Y,如果X与Y的叉积>0,那么X就在Y的顺时针方向,这样就可以判断啦>o<
还拿这个图举栗子吧 (o’.’o)
我们在栈中加入了HK这条线段,接着加入C,又加入D,诶??==,好像不太对,KC和CD的叉积好像是正(我们是顺时针扫描),退栈!!!
然后依次考虑后面的点………………….
求完了凸包怎么求最远点呢??穷举??我试过了,1063MS虽然没有TLE,但是这个方法太不优美了是不是,所以我们要请出旋转卡壳这个神奇的方法,感觉名字很高冷>_<,其实原理很简单………
还是看图吧:
我们以凸包上的每条线段为底,按照一定顺序依次枚举其它点,用叉积来求这个点和这条线段构成的三角形的面积,当j构成的三角形的面积大于j+1构成的三角形的面积时,我们就找到了最大面积的三角形,也就找到了到这条线段距离最远的点……….
第一次写,当然是借鉴的其他人哒o(╯□╰)o >_<
很优美的算法,总复杂度为O(nlogn),32MS轻松上榜
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Max(a,b,c) max(a,max(b,c))
using namespace std;
const int maxn=50000+5;
int n,low,num,hull[maxn];
struct point{
int x,y;
}s[maxn];
inline int read(void){
char ch=getchar();
int f=1,x=0;
while(!(ch>='0'&&ch<='9')){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
return f*x;
}
inline int cross_product(point a,point b){//叉积
return a.x*b.y-b.x*a.y;
}
inline int calclen(point a){//向量长度
return a.x*a.x+a.y*a.y;
}
inline point produce_cross(point a,point b){//计算向量
point cross;
cross.x=a.x-b.x,cross.y=a.y-b.y;
return cross;
}
inline bool cmp(const point &a,const point &b){
point crossa=produce_cross(a,s[1]),crossb=produce_cross(b,s[1]);
int cp=cross_product(crossa,crossb);
if(cp>0||(cp==0&&calclen(crossa)<calclen(crossb)))
return true;
return false;
}
int Graham_scan(){//求凸包上的点及其个数
int top=2;
sort(s+2,s+n+1,cmp);
hull[1]=1,hull[2]=2;
for(int i=3;i<=n;i++){
while(top>=2&&cross_product( produce_cross(s[hull[top]],s[hull[top-1]]),produce_cross(s[i],s[hull[top]]) )<=0 )//叉积<=0代表逆时针方向,我们是逆时针加点滴
top--;
hull[++top]=i;
}
return top;
}
int rotating_caliper(){
int dis=0,j=3;
s[num+1]=s[1],s[num+2]=s[2];
for(int i=1;i<=num;i++){
while(cross_product( produce_cross(s[i+1],s[i]),produce_cross(s[j],s[i]) )<cross_product( produce_cross(s[i+1],s[i]),produce_cross(s[j+1],s[i]) )){
j++;
if(j>num)
j=j-num;
}
dis=Max(dis,calclen(produce_cross(s[i],s[j])),calclen(produce_cross(s[i+1],s[j])));
}
return dis;
}
signed main(void){
n=read(),low=1;
for(int i=1;i<=n;i++){
s[i].x=read(),s[i].y=read();
if((s[low].y==s[i].y&&s[low].x>s[i].x)||s[low].y>s[i].y)
low=i;
}
swap(s[low],s[1]);
num=Graham_scan();
for(int i=1;i<=num;i++)
s[i]=s[hull[i]];
printf("%d\n",rotating_caliper());
return 0;
}
by >o< neighthorn