旋转卡壳小结

介绍

基本问题是求出一个凸多边形上距离最远的两点的距离,即求出最远的一对对踵点。

对踵点: 对于两个点,假如可以分别过这两个点作两条平行线,使凸多边形上的其他所有点都在这两条平行线之间,那么称这两个点为对踵点。

拓展的话还可以求很多东西,但是原理都是差不多的。

做法

简单来说,就是用一对平行线卡住凸包,然后将凸包进行旋转。

有两种情况,一种是卡住两个点,一种是卡住一个点和一条边,像这样:
在这里插入图片描述
考虑卡住一条边和一个点的情况,可以发现,卡住的这个点离这条边最远,并且,这个点和这条边形成的三角形面积最大。

于是,我们就找到了两个对踵点对(也可能是一个),即边的两个端点和卡住的那个点。

于是,我们对于每条边,找到形成三角形面积最大的那个点,就是被卡住的那个点了。

这样一看,似乎是 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);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/106997104