计算几何之凸包(一) {卷包裹算法}
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
计算几何之凸包(一) {卷包裹算法}
{
半个寒假都在写凸包
这几篇文章整理一下
主要介绍二维凸包的求解算法
以及一个简单的应用
}
================================================== ==================
一.凸集&凸包
(下文中所有的集合若不作特殊说明都是指欧氏空间上的集合)
凸集(Convex Set):任意两点的连线都在这个集合内的集合就是一个凸集.
A set in Euclidean space is convex set if it contains all the line segm ents connecting any pair of its points.
/Convex.html
凸包(Convex Hull):包含集合S的所有凸集的交集就是集合S的凸包.
The convex hull of a set of points S in N dimensions is the intersection of all convex sets containing S.
我们经常关注一个点集的凸包这也是计算几何学的一个基本问题
我们现在已经有成熟的算法可以求出平面点集的凸包和空间点集的凸包
甚至有的算法可以方便的求出任意维度欧氏空间内的一个点集的凸包
凸包有着优美而实用的性质我们可以利用凸包把一个杂乱的点集所包含的信息进行有效的概括梳理
================================================== ==================
二.平面点集的凸包的算法
(下文中所有凸包若不作特殊说明都是指平面点集的凸包)
有两种直观的理解凸包的方式
在木板上钉钉子用一个有弹性的橡皮筋去框住所有钉子
橡皮筋形成的图形就是这个钉子所构成的点集的凸包
还有一种理解我们用一根麻绳绑住一个外面的钉子然后拉着麻绳绕所有钉子一圈这个麻绳最后也构成了点集的凸包
其中第二种理解是我们一个经典算法卷包裹法(Gift Wrapping)的思路
卷包裹算法从一个必然在凸包上的点开始向着一个方向依次选择最外侧的点
当回到最初的点时所选出的点集就是所要求的凸包
这里还有两个问题不是很清楚:
1.怎么确定一个肯定在凸包上的点?
这个问题很好解决取一个最左边的也就是横坐标最小的点
如果有多个这样的点就取这些点里纵坐标最小的
这样可以很好的处理共线的情况
2.如何确定下一个点(即最外侧的点)?
我们需要利用向量的叉积来解决这个问题
-----------------------------------------------------------------------
向量的叉积(Cross Product)原本是三维空间中的问题在二维中也有巧妙的应用/CrossProduct.html
(下文中所有的叉积若不作特殊说明都是指二维中新定义的叉积
下文中所有的向量乘法若不作特殊说明都是指向量的叉积)
我们定义二维向量
1.叉积的一半是一个三角形的有向面积
这个公式可以避免面积计算的误差如果点是整点那么所有运算都是整数2.向量的叉积的符号代表着向量旋转的方向
向量的叉积是不满足交换律的
向量A乘以向量B 如果为正则为A逆时针旋转向B 否则为顺时针
当然这里A转向B的角总是考虑一个小于180度以内的角否则就会出错----------------------------------------------------------------------- 有了向量我们就可以选取一个最外侧的点了
比如现在我们卷包裹卷到J点我们要选取一个最外侧的点
当然比较红色的到角可以直接得到最外侧的点不过不方便
我们可以考虑那个蓝色的到角
利用向量我们可以比较哪个点"更外侧"
比如点K和点I 我们利用向量JK乘以向量JI得到一个数这个数应该是负数说明I比K更外侧
两个向量的比较具有传递性所以我们可以像N个数里取最大的数一样取出最外侧的
遍历所有点每个点都和现有最外侧的点比较得到新的最外侧的点
至此两个问题都得以解决我们可以写出满足一般要求的卷包裹算法了
不过还遗留有一个问题就是处理共线的问题
有时候我们需要凸包边上的点也考虑到有时候却需要去掉这些点
我们通常称在凸包顶点处的点为极点
如果我们只要求保留极点而去除在边上的点
我们只需在取外侧的点的时候碰到共线的点取最远的
相反如果我们要保留所有在边上的点我们只需要在共线的点中取最近的
这样整个卷包裹法终于完成了
给出完整的代码:
GiftWrapping
{$inline on}
const zero=1e-6; maxn=100000;
type point=record x,y:extended; end;
var p:array[1..maxn]of point;
ch:array[1..maxn]of longint;
temp,n,m,i,j,k:longint;
function sgn(x:extended):longint; inline;
begin
if abs(x) if x<0then sgn:=-1else sgn:=1; end; function cross(a,b,c:point):extended; inline; begin cross:=(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); end; function dist(a,b:point):extended; inline; begin dist:=sqr(a.x-b.x)+sqr(a.y-b.y); end; function cmp(a,b,c:point):boolean; inline; var temp:longint; begin temp:=sgn(cross(a,b,c)); if temp<>0then exit(temp<0); {*B} cmp:=dist(a,b) end; begin assign(input,'Hull.in'); reset(input); assign(output,'Hull1.out'); rewrite(output); readln(n); for i:=1to n do begin readln(p[i].x,p[i].y); if (j=0)or(p[i].x (sgn(p[i].x-p[j].x)=0)and(p[i].y end; temp:=j; while true do begin k:=0; inc(m); ch[m]:=j; for i:=1to n do if (i<>j)and((k=0)or