Luogu_P2742 圈奶牛 Fencing the Cows /【模板】二维凸包

圈奶牛 Fencing the Cows

链接

Luogu_P2742 圈奶牛 Fencing the Cows /【模板】二维凸包

题目描述

农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏。他建造的围栏必须包括他的奶牛喜欢吃草的所有地点。对于给出的这些地点的坐标,计算最短的能够围住这些点的围栏的长度。

输入格式

输入数据的第一行是一个整数。表示农夫约翰想要围住的放牧点的数目 nn。

2 2 2到第 ( n + 1 ) (n+1) (n+1)行,每行两个实数,第 ( i + 1 ) (i+1) (i+1)行的实数 x i , y i x_i, y_i xi,yi分别代表第 i i i个放牧点的横纵坐标。

输出格式

输出输出一行一个四舍五入保留两位小数的实数,代表围栏的长度。

输入输出样例

输入 #1
4
4 8
4 12
5 9.3
7 8
输出 #1
12.00

说明/提示

数据规模与约定

对于 100 % 100\% 100%的数据,保证 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105 − 1 0 6 ≤ x i , y i ≤ 1 0 6 -10^6 \leq x_i, y_i \leq 10^6 106xi,yi106。小数点后最多有 2 2 2位数字。

思路

这题是听完课之后做的模板题。

凸包(Convex Hull)是一个计算几何(图形学)中的概念。 在一个实数向量空间 V V V中,对于给定集合 X X X,所有包含 X X X的凸集的交集 S S S被称为 X X X的凸包。 X X X的凸包可以用 X X X内所有点( X 1 , . . . , X n X_1,...,X_n X1,...,Xn)的凸组合来构造。 在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有点的。(引用自百度百科)

本题要我们求的就是农场上所有放牧点的凸包的长度。

解二维凸包问题常用的方法是:

  • 找到一个 y y y坐标最小( y y y坐标相同就找 x x x坐标最小)的点,作为凸包的起点;
  • 把剩余的每个点分别与起点联结,并判断这条线段所在的直线与 x x x轴的夹角的大小,按照这个夹角的大小把每个点从小到大排序;
  • 把每个点依次加入栈,对于每个即将入栈的点 P i P_i Pi,判断线段 P i P t o p P_iP_{top} PiPtop是否在线段 P t o p P t o p − 1 P_{top}P_{top-1} PtopPtop1的逆时针方向,如果是,则将点 P i P_i Pi加入栈,否则,弹出 P t o p P_{top} Ptop,如此循环,直至 P i P_i Pi入栈;
  • 最后,栈中的每个点连起来就是这些点的凸包;
  • 这个算法叫做Garham算法;

判断线段 A B AB AB是否在线段 C D CD CD的逆时针方向,我们要用到叉积。

设点 A , B , C , D A,B,C,D A,B,C,D x , y x,y x,y坐标分别为 A . x , A . y , B . x , B . y , C . x , C . y , D . x , D . y A.x,A.y,B.x,B.y,C.x,C.y,D.x,D.y A.x,A.y,B.x,B.y,C.x,C.y,D.x,D.y,那么线段 A B AB AB C D CD CD所在向量的叉积的计算公式就是:
P ( A , B , C , D ) = ( B . x − A . x ) ∗ ( D . y − C . y ) − ( D . x − C . x ) ∗ ( A . y − B . y ) P(A,B,C,D)=(B.x-A.x)*(D.y-C.y)-(D.x-C.x)*(A.y-B.y) P(A,B,C,D)=(B.xA.x)(D.yC.y)(D.xC.x)(A.yB.y)

如果 P ( A , B , C , D ) = 0 P(A,B,C,D)=0 P(A,B,C,D)=0,那么 A B AB AB就与 C D CD CD平行;
如果 P ( A , B , C , D ) > 0 P(A,B,C,D)>0 P(A,B,C,D)>0,那么 A B AB AB就在 C D CD CD的逆时针方向;
如果 P ( A , B , C , D ) > 0 P(A,B,C,D)>0 P(A,B,C,D)>0,那么 A B AB AB就在 C D CD CD的顺时针方向;

这样,我们就可以完成算法中两条线段、直线之间的关系的判断了。

代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
typedef long double ld;
typedef long long ll;
const ld inf=100000000.0;
struct poi{
	ld x,y;
}p[100001],a[100001];
ll n,cnt,beg;
ld ans;
stack<poi> s;
ll cro_pro(poi a,poi b,poi c,poi d)//求向量叉积(cross product) 
{
	return (b.x-a.x)*(d.y-c.y)-(d.x-c.x)*(b.y-a.y);
}
ld len(poi a,poi b)//求AB边长
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(poi a,poi b)//按照与x轴夹角的大小排序
{
	if(cro_pro(p[1],a,p[1],b)>0)
		return true;
	else if(cro_pro(p[1],a,p[1],b)==0&&len(p[1],a)<len(p[1],b))
		return true;
	else
		return false;
}
void garham()
{
	poi a,b,c;
	s.push(p[1]);
	s.push(p[2]);
	for(ll i=3;i<=n;i++)
	{
		c=p[i];
		b=s.top();
		s.pop();
		a=s.top();//注意要去除栈顶的前两个元素
		while(cro_pro(a,b,b,c)<0)
		{
			b=s.top();
			s.pop();
			a=s.top();
		}
		s.push(b);
		s.push(c); 
	}
}
int main()
{
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++)
	{
		scanf("%Lf%Lf",&p[i].x,&p[i].y);
		if(p[i].y<p[1].y)
			swap(p[i],p[1]);
		else if(p[i].y==p[1].y&&p[i].x<p[1].x)
			swap(p[i],p[1]);
	}
	sort(p+1+1,p+n+1,cmp);
	garham();
	while(!s.empty())
	{
		cnt++;
		a[cnt]=s.top();
		s.pop();
	}
	for(ll i=1;i<cnt;i++)
		ans+=len(a[i],a[i+1]);
	ans+=len(a[cnt],a[1]);
	printf("%.2Lf",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值