这是一个很菜的 Oier 的博客|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-08-12 15:08阅读: 67评论: 0推荐: 0

P1452 [USACO03FALL]Beauty Contest G /【模板】旋转卡壳

[USACO03FALL]Beauty Contest G /【模板】旋转卡壳

题目描述

给定平面上 n 个点,求凸包直径。

输入格式

第一行一个正整数 n
接下来 n 行,每行两个整数 x,y,表示一个点的坐标。

输出格式

输出一行一个整数,表示答案的平方。

样例 #1

样例输入 #1

4
0 0
0 1
1 1
1 0

样例输出 #1

2

提示

【数据范围】
对于 100% 的数据,2n50000|x|,|y|104


upd 2022.7.22:新增加四组 Hack 数据。

Solution

首先需要知道,平面点集中的最远点对一定是在这个点集的凸包上的(用三角形和延长线去证明),具体证明过程就不写了(我懒)。所以先对这些点跑个 Graham 求凸包,把凸包存储到一个栈 s 中。

那么该如何在凸包上求这组最远点对呢?想象有两把尺子,将它们摆到一个相互平行的位置,然后将尺子卡到凸包上,这样是不是就卡出来了对于凸包上的某一个点来说的最远点?接着将尺子继续旋转,卡到一个新的边上,这样又得到了新的一个点的最远点。就这样旋转一圈就得到了每个点的最远点,取这些距离值的最大值就是答案。引用一张图来说明就是这样的(这张图应该够形象了):

假设已经知道了一把尺子的位置需要找到另一把尺子,如果直接枚举 n 个点的话时间复杂度为 O(n2),是无法承受的。但是有一点极其重要的性质就是,当前尺子旋转卡到下一个边的时候,另一把尺子只会在上一个的位置的基础上继续转,而不会往回转。这样只要第二把尺子接着上一次继续转,时间复杂度就降成了 O(n)

那么如何判断当前点是否是最远点呢,这就可以用到向量叉乘的性质了。假设目前尺子卡着的边是 (i,i+1),另一把尺子卡着的点是 j 的话,直接用 PiPi+1×Pi+1Pj 计算三角形面积即可,因为下一个可以卡住的点是 j+1,所以同样计算下一个三角形的面积然后进行比较即可(因为两个三角形是同底的,所以面积的大小关系就是高的大小关系)。

Code

同样是采用结构体表示向量和点,并且重载运算符。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
const int _SIZE=5e4;
struct VEC{
	int x,y;
	int operator* (const VEC &a) const {return x*a.y-a.x*y;}
	VEC operator- (const VEC &a) const {return (VEC){x-a.x,y-a.y};}
}p[_SIZE+5];
int n;
int check(VEC a,VEC b) {return a*b;}
int dis(VEC a,VEC b) {return pow(a.x-b.x,2)+pow(a.y-b.y,2);}
bool cmp(VEC a,VEC b)
{
	int temp=check(a-p[1],b-p[1]);
	if (temp) return temp>0;
	return dis(a,p[1])<dis(b,p[1]);
}
VEC s[_SIZE+5];int cnt=0;
int Calc()
{
	if (cnt<4) return dis(s[1],s[2]);
	int j=3;int res=0;
	for (int i=1;i<cnt;i++)//这里只能到cnt-1不能到cnt,因为当i=cnt时i+1就是一个不存在的点,会导致一些离谱错误
	{
		res=max(res,dis(s[i],s[i+1]));
		while (check(s[i+1]-s[i],s[j]-s[i+1]) <= check(s[i+1]-s[i],s[j%cnt+1]-s[i+1])) j=(j%cnt)+1;
		res=max(res,max(dis(s[i],s[j]),dis(s[i+1],s[j])));
	}
	return res;
}
int main()
{
	read(n);
	for (int i=1;i<=n;i++) 
	{
		scanf("%d%d",&p[i].x,&p[i].y);
		if (p[i].y<p[1].y || (p[i].y==p[1].y && p[i].x<p[1].x)) swap(p[1],p[i]);
	}
	sort(p+2,p+n+1,cmp);
	s[++cnt]=p[1];
	for (int i=2;i<=n;i++)
	{
		while (cnt>1 && check(s[cnt]-s[cnt-1],p[i]-s[cnt])<=0) cnt--;
		s[++cnt]=p[i];
	}
	s[++cnt]=p[1];//这里需要将p[1]加入凸包以方便旋转卡壳的时候可以卡住最后一个点
	printf("%d\n",Calc());
	return 0;
}
posted @   Hanx16Msgr  阅读(67)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起