介绍
基本问题是求出一个凸多边形上距离最远的两点的距离,即求出最远的一对对踵点。
对踵点: 对于两个点,假如可以分别过这两个点作两条平行线,使凸多边形上的其他所有点都在这两条平行线之间,那么称这两个点为对踵点。
拓展的话还可以求很多东西,但是原理都是差不多的。
做法
简单来说,就是用一对平行线卡住凸包,然后将凸包进行旋转。
有两种情况,一种是卡住两个点,一种是卡住一个点和一条边,像这样:
考虑卡住一条边和一个点的情况,可以发现,卡住的这个点离这条边最远,并且,这个点和这条边形成的三角形面积最大。
于是,我们就找到了两个对踵点对(也可能是一个),即边的两个端点和卡住的那个点。
于是,我们对于每条边,找到形成三角形面积最大的那个点,就是被卡住的那个点了。
这样一看,似乎是 O ( n 2 ) O(n^2) O(n2) 的复杂度,但是这里其实是有单调性的。
当多边形顺时针旋转,使左边的平行线卡住下一条边时,右边的红线卡住的点,一定在原来卡住的点的逆时针方向。
所以逆时针枚举下一条边时,点在上一次基础上往逆时针方向找即可,时间复杂度 O ( n ) O(n) O(n)。
代码如下:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 50010
#define inf 999999999
#define ll long long
int n,m=0,t=0;
struct point{
ll x,y; point(ll xx=0,ll yy=0):x(xx),y(yy){
}
point operator -(const point &b){
return point(x-b.x,y-b.y);}
ll operator *(const point &b){
return x*b.y-y*b.x;}
};
point a[maxn],b[maxn],S,zhan[maxn];
ll dis(point x,point y){
return (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y);}
bool cmp(point x,point y){
return x*y>0||(x*y==0&&dis(point(0,0),x)<dis(point(0,0),y));}
int main()
{
scanf("%d",&n);S.y=inf;
for(int i=1;i<=n;i++){
scanf("%lld %lld",&a[i].x,&a[i].y);
if(a[i].y<S.y||(a[i].y==S.y&&a[i].x<S.x)){
if(S.y!=inf)b[++m]=S;
S=a[i];
} else b[++m]=a[i];
}
if(m==1){
printf("%lld\n",dis(b[1],S));return 0;}
for(int i=1;i<=m;i++)b[i].x-=S.x,b[i].y-=S.y;
sort(b+1,b+m+1,cmp);
zhan[++t]=point(0,0); zhan[++t]=b[1];
for(int i=2;i<=m;i++){
while(t>1&&(zhan[t]-zhan[t-1])*(b[i]-zhan[t-1])<=0)t--;
zhan[++t]=b[i];
}
int j=2,J=j+1; ll ans=0;
for(int i=1;i<t;i++){
while((zhan[j]-zhan[i+1])*(zhan[i]-zhan[i+1])<(zhan[J]-zhan[i+1])*(zhan[i]-zhan[i+1]))j=J,J=j==t?1:j+1;
ans=max(ans,max(dis(zhan[j],zhan[i]),dis(zhan[j],zhan[i+1])));
}
printf("%lld",ans);
}