计算几何之 凸包 Andrew算法 代码模板与实现过程

凸包的定义

啥是凸包呢?我们不严谨地把这个词拆开来看,凸是指凸多边形的意思,包是指包住所有的点,因此凸包就是一个包住所有的点的凸多边形。简单来说,就是给你n个点,将这n个点的最外层的点连起来,将所有的点包在内部,这就是这n个点的凸包。可以想象这n个点都插了一根柱子,然后用一个橡皮筋将所有的柱子套住,然后再松开,橡皮筋所包住的图形就是这个凸多边形。

因此这里凸包有一个性质:凸包是平面中包括住所有点的多边形中周长最小的。


凸包的求法

凸包的求法有很多种,有Graham扫描法、Jarvis步进法等等,掌握一个就行,这里讲一个简单一点的方法Andrew算法。
Andrew算法是Graham算法的改良版,好写一点,据说比Graham快一点。Andrew并不难,就两步:

第一步: 首先将所有点排个序,以x坐标为第一关键字,以y坐标为第二关键字。
第二步: 从左至右维护上半部分的边界,从右至左维护下半部分的边界。

那么问题的关键就是如何维护来找到这个边界呢?这里我们用栈来维护,找上边界时,我们从第一个点也就是最左边的点出发,然后一次枚举下一个点,如果下一个点在栈顶向量延长线的左侧(注意这里栈顶向量指的是栈顶第二个点指向站顶点的向量),那么我们就删去栈顶这个点将新点入栈;若下一个点在栈顶那个向量延长线的右侧,就不用将栈顶点出栈,直接将新点入栈即可。这样找到最右边的点时,上边界就确定完了。注意这里的出栈是个while过程,当栈顶点在左侧时,一直删掉栈顶点,直到在右侧为止。
然后我们在按照相同的方法找下边界,我们从右向左找,若新点在栈顶向量延长线的左侧,则栈顶的点出栈,新点入栈;若新点在右侧,则不用出栈,直接将新点入栈即可。注意这里从右边往左边找的时候要直到最左边的点后才结束,也就是说也要判断最开始走的第一个点。具体如下图:
eg
若出现第①种情况,就是求上边界时,下一个点在延长线的右侧时,直接将新点入栈;若是第②种情况,下一个点在延长线的左侧时,先将栈顶点出栈,再将新点入栈,因为上一个点一定会被新点包含进去。
eg

下边界同理,如上图存在两种情况,在右侧的直接入栈,左侧的先将栈顶点出栈再入栈。
而到最后一定要将第一个点也就是出发点判断一下,不然有可能会出现下图这种情况:
eg
凸包并不是黑色线条的图形,最后两条边应该是蓝色的边,这里就需要队出发点进行一次判断。

Andrew的步骤就是这两步,有些人会有疑问:如何判断一个点在该直线的左侧还是右侧,就是最常用的向量法,用两向量的叉积正负值来判断在左侧还是在右侧,如图,我们只需要判断向量u和v的叉积,若为正,则在左侧;若为负,则在右侧。
eg
那么如果下一个点就在延长线上,那么出不出栈就看题目要求了,若要找所有点,那就不出栈;若要找最少点数,那就出栈。


代码模板

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 10010;
typedef pair<double,double> PDD;

int n,top,stk[N];	//手动开一个栈stk,top是栈顶指针
PDD p[N];		//用pair来存点
bool used[N];		//判断某点是否被用过,当作边界

double lens(PDD a,PDD b)	//求ab两点距离的函数
{
	double x = a.first - b.first;
	double y = a.second - b.second;
	return sqrt(x*x + y*y);		//距离=根号下x、y坐标的平方和
}

PDD operator-(PDD a,PDD b)	//重载一下减号,可以用在两个pair间直接减
{
	return {a.first-b.first,a.second-b.second};
}

double cross(PDD a,PDD b,PDD c)		//计算两个向量的叉积
{
	PDD u,v;	//定义u、v两个向量
	u = b - a,v = c - a;	//u是b-a,v是c-a
	return u.first*v.second - v.first*u.second;	//叉积是x1*y2 - x2*y1
}

double andrew()		//andrew算法
{
	sort(p,p+n);	//第一步先对点进行排序,pair存点的好处就是好排序
	for(int i = 0;i < n;i++)	//从左往右维护每一个点
	{
		while(top >= 2 && cross(p[stk[top-1]],p[stk[top]],p[i]) > 0)	//如果栈里元素大于等于两个并且叉积大于0,注意是while
		{
			used[stk[top]] = 0;	//取消用了的标记
			top--;	//先出栈
		}
		stk[++top] = i;		//新点入栈
		used[i] = 1;		//标记该点用了
	}
	used[0] = 0;	//取消第一个点的标记,我们要再判断一下第一个点
	for(int i = n-2;i >= 0;i--)	//从右往左维护每个点
	{
		if(used[i])	//如果某点用作上边界了,直接跳过
			continue;
		while(top >= 2 && cross(p[stk[top-1]],p[stk[top]],p[i]) > 0)	//如果栈里元素个数大于等于2且叉积大于0
			top--;		//先出栈
		stk[++top] = i;		//新点入栈
	}

	double perimeter = 0;	//周长
	for(int i = 2;i <= top;i++)	//循环每个点,这里栈里是从1开始存的
		perimeter += lens(p[stk[i]],p[stk[i-1]]);	//计算两点间距离,加和
	return perimeter;	//返回周长
}

int main()
{
	cin >> n;	//n个点
	for(int i = 0;i < n;i++)
	{
		double x,y;
		cin >> x >> y;		//输出n个点的坐标
		p[i] = {x,y};
	}

	double ans = andrew();		//andrew算法求凸包
	printf("%.2lf\n",ans);		//输出凸包周长

	return 0;
}

经典例题

题解传送门:Acwing 2935:信用卡凸包

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算几何算法是指用于解决与几何相关的问题的一系列数学算法。它涵盖了几何图形的构造、变换、相交、碰撞检测、射线追踪等方面。计算几何算法实现通常需要在计算机程序中编写相关的代码实现计算几何算法通常可以通过编写程序来实现。自定义计算几何算法实现过程可能会涉及到各种数学运算、数据结构和算法设计。常见的计算几何算法实现包括点的距离计算、线段的相交检测、多边形的包围盒计算等。 实现计算几何算法过程中,我们通常会使用到数学中的向量、矩阵、多边形等概念。另外,我们还会用到很多常见的数学公式和算法,如直线方程、圆方程、向量的内积和叉积等等。这些数学知识的运用和实现实现计算几何算法的基础。 编写计算几何算法代码时,我们可以使用各种编程语言,如C++、Python、Java等。在代码中,我们需要定义适当的数据结构来存储几何图形的信息,如点、线段、多边形等。同时,我们也需要实现各种相应的算法函数来进行计算和处理。 对于大型项目或需要高效计算的情况,我们可能需要使用更高效的算法和数据结构,如凸包算法、平面分割树等。这些算法和数据结构的选择会影响到计算几何算法实现效率和效果。 总之,计算几何算法实现的pdf主要是介绍了计算几何算法的概念和实现的基本过程。通过学习和掌握计算几何算法,我们可以更好地处理与几何相关的问题,并能够应用到各种领域,如计算机图形学、计算机辅助设计等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值