POj2187 【模板】旋转卡壳 / 选美大赛

这篇博客讲述了农夫约翰的牛Bessie在赢得'牛世界小姐'后,计划参观世界各地的农场。博客讨论了如何计算农场间的最大直线距离,以确保Bessie的手提箱能装足食物。给出了输入输出格式、样例和问题分析,并提供了作者的解决方案代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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; 
}

太丑的话见谅见谅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值