ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【学习笔记】二维凸包

2022-07-20 21:05:15  阅读:167  来源: 互联网

标签:Cartesian point double top 笔记 凸包 二维 极角


只会二维凸包,其他的都不会

概述

凸包是啥

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

当然,这不重要,凸包一般长这样:image$$图1.1$$

凸包在OI中的应用远超计算几何的范畴,除了一些对求凸包的显性考察外,用数据结构维护动态凸包,斜率优化等中都有所涉及。

然而,这里只会讲凸包的计算几何算法

例题

P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

算法实现

Graham扫描法

首先找到最靠近左下的那个点,这个点一定在凸包上,不然就不是把最外层围起来了。
其中,左下强调下,即以 \(y\) 轴为第一关键字。

image

\[图2.1 \]

如上图,最靠近左下的是点E,而不是点A。


然后,以这个点为极点,其他点按照极角排序。

这里先介绍一个黑科技:\(atan2\) 函数
atan2(x, y) 返回 \(tanα\) \(=\) \(\frac{x}{y}\) 中的 \(α\),同时满足 \(α\) \(\in \left [ -\pi , \pi \right ]\)。
当 \(y = 0\) 时, \(tanα\) 不存在,返回 \(0\) , 当 \(x = 0\) 时,返回 \(-1\)。

image

\[图2.2.1 \]

在上图中,每个点的极角是由点E到其他的点连成的直线 与 点E所在的与 \(x\) 轴平行的直线所成的夹角。
比如这个:image

\[图2.2.2 \]

是点F的极角。

image

\[图2.2.3 \]

这个是点G的极角。

还是看上图2.2.1,排序结果是FBCADG,可以理解成从0度逆时针扫一圈。


之后,按照顺序依次访问所有点,判断可行性。

用图来讲:
image

\[图2.3.1 \]

先把极角最小的E丢到凸包的栈里边,准备开始扫描。

image

\[图2.3.2 \]

检查B是否在F的一侧(检查是不是凸多边形)。
这里检查到B可行,先加入到栈中。

image

\[图2.3.3 \]

检查到C更加靠近外侧(如果加入B就会形成凹多边形,显然B在凸包中,而C不在)
然后把B点弹出栈,判断E和C的关(同判断B)

image

\[图2.3.4 \]

依次这么判断,最后所有凸包上的点都会在栈中
image

\[图2.3.5 \]

继续解决一些细节上的问题: 怎么计算一个节点是否在前一个点的一侧。
这就需要引入一个数学上的东西:叉积

image

\[图2.3.6 \]

\(\vec{p_{1}}\ast\vec{p_{2}}\) 即为平行四边形面积。

原理不会。看这个blog

公式:\(\vec{p_{1}}\ast\vec{p_{2}} = {x_{1}}{y_{2}} - {x_{2}}{y_{1}}\)。
换种方法表示就是 \(|a|·|b|·sin<a,b>\)。

所以,若这两个向量的叉积≥0 证明这两个向量平行或者夹角是个锐角,那某个点一定不在凸包边上。

Code

#include<cmath> 
#include<cstdio>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int MAXN = 1e5 + 10;
const double INF = 1145141919810;
int n, top;
double ans;

struct Cartesian{
	double x, y;
}point[MAXN], s[MAXN];

inline bool cmp(const Cartesian &a, const Cartesian &b){
	double A = atan2((a.y - point[1].y), (a.x - point[1].x));
	double B = atan2((b.y - point[1].y), (b.x - point[1].x));

	if(A != B) return A < B; //按极角的大小递增排序 
	else if(a.x != b.x) return a.x < b.x;  //如果极角相同,x坐标小的点在前
	else return a.y < b.y; //如果极角和x坐标都相同,y坐标小的在前 
}

double Get_Dis(Cartesian a, Cartesian b){
	return sqrt((1.0 * (a.x - b.x) * (a.x - b.x)) + (1.0 * (a.y - b.y) * (a.y - b.y)));
} //计算两点间距离

double Get_Cross(Cartesian a, Cartesian b, Cartesian c){
	return 1.0 * (b.x - a.x) * (c.y - a.y) - 1.0 * (b.y - a.y) * (c.x - a.x);
} //计算两向量的叉积

void Get_Bag(){ //造凸包 
	Cartesian point_left = (Cartesian){INF, INF};
	int pos;
	for(register int i = 1; i <= n; i++){
		if(point[i].y < point_left.y || (point[i].y == point_left.y && point[i].x < point_left.x)){
			point_left = point[i];
			pos = i;
		}
	} //寻找最左下角的点 
	swap(point[pos], point[1]);

	sort(point + 2, point + 1 + n, cmp); //按极角顺序排序 
	s[++top] = point[1], s[++top] = point[2];
	for(register int i = 3; i <= n;){
		if(top >= 2 && Get_Cross(s[top - 1], point[i], s[top]) >= 0)
			top--;
		else s[++top] = point[i++];
	}
	s[0] = s[top];
}

int main(){
	scanf("%d", &n);
	for(register int i = 1; i <= n; i++)
		scanf("%lf%lf", &point[i].x, &point[i].y);
	
	Get_Bag();

	while(top >= 1){
		Cartesian p1 = s[top];
		Cartesian p2 = s[top - 1];

		ans += Get_Dis(p1, p2);
		top--;
	}

	printf("%.2lf", ans);

	return 0;
} 



Andrew算法

不会,咕咕咕。

标签:Cartesian,point,double,top,笔记,凸包,二维,极角
来源: https://www.cnblogs.com/TSTYFST/p/16499792.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有