Appearance
凸包基础
定义
凸集与凸多边形
若集合
凸包
点集
有限点集的凸包允许退化:空集的凸包按约定为空,单点的凸包是该点,两点或全体点共线时凸包是线段;只有存在至少三个不共线点时,凸包才是具有正面积的凸多边形。
极点
对于凸集
支持线使整个凸集位于它的一个闭半平面内。支持线与凸集的交集可能是一个极点,也可能是一整条边;只有交集为单点时,该点才是这个方向暴露出的极点。因此,仅知道“其余点位于同一个闭半平面”而允许其它点落在支持线上,不能作为极点的充分判据;若其它不同坐标的点都严格位于同一个开半平面,则该支持线只接触
极边
对于二维非退化凸包,极边是支持线与凸包相交得到的完整一维面。支持线上的共线中间点可以位于极边上,但极边的两个端点才是极点。
求凸包
极点法
枚举每个点,判断每个点是否是极点。
依据:先删除重复坐标。一个点不是极点,当且仅当它属于其它点的凸包。根据 Carathéodory 定理,平面中的该点一定属于至多三个其它点的凸包;这里必须检查闭三角形及其边界,并允许退化为线段,不能只检查严格的三角形内部。
时间复杂度:
极边法
枚举两点确定一条直线,判断它是否是凸包的支持线。
依据:其它点在该直线的同一个闭半平面内。若支持线上还有其它共线点,应取这一维面的两个最远端点作为规范凸包的极边,不能把支持线上的任意点对都输出成边。
时间复杂度:
增量法
每次用一个点去更新凸包。
- 若点在凸包内,舍弃
- 若点在凸包外,找凸包的切线
可推广至三维凸包、动态凸包。
若当前凸包有
找凸包的切线:切点的前驱和后继都在对应射线的同一侧,暴力需要
依次插入
对于已经按逆时针顺序给出并完成规范化的静态凸多边形,预处理
Gift wrapping
先删除重复坐标,从一个确定的极点出发;每一步遍历其它点,选择下一条极边。
若同一方向上有多个共线候选,只输出真正极点时必须选择离当前点最远者,否则会把边内部点当成下一顶点。空集和单点直接返回,全共线点集返回线段的两个端点。
时间复杂度为
起点可以取字典序最小点等确定的坐标极值端点。若某一坐标极值由一整条极边取得,应再用另一坐标选择该极边的一个端点,而不是任取边内部点。
Graham scan
基于极角排序的求凸包算法。下面默认返回按逆时针排列、首点不在末尾重复的真正极点,不保留极边内部的共线点。首先按坐标删除重复点;设去重后点数为
算法流程:
- 取纵坐标最小、再取横坐标最小的点作为基点;
- 将其它点按相对基点的极角排序,同极角时按到基点的距离递增;
- 依次处理排序后的点。当栈顶两个点与新点不构成严格逆时针转向,即叉积小于等于
时,弹出栈顶; - 将新点入栈,最终得到只含真正极点的凸包。全共线输入最终只保留两个端点。
若题目要求保留所有边界共线点,不能只把弹栈条件机械改成“叉积小于
时间复杂度:
Andrew’s algorithm
基于坐标字典序排序的求凸包算法。输出约定与 Graham scan 相同。
以最左最右两点为界,凸包可以分为上凸包和下凸包两部分。
算法流程:
- 对点集以横坐标为第一关键字、纵坐标为第二关键字排序,并删除重复坐标;
- 去重后点数为
时直接返回; - 正序遍历各点维护下凸壳,倒序遍历各点维护上凸壳。若只保留真正极点,当末尾两点与新点的叉积小于等于
时弹出末点; - 拼接上下凸壳时,各自删除一个重复端点,且最终不重复首点。全共线输入返回字典序最小、最大的两个端点。
若要保留边界共线点,通常只在叉积小于
时间复杂度:
凸包进阶
凸包二分
判断点是否在凸多边形中
以下假设凸多边形有
按横坐标(x)二分:
确定最左、最右端点
单次查询的时间复杂度为
利用极角序二分:
固定一个顶点
已有规范凸多边形时,检查或建立上述索引需要
判断直线是否与凸多边形相交
设直线为
规范化预处理后,单次查询为
过一点求凸多边形切线
本节讨论从严格位于凸多边形外部的点
对于严格外点,切点处相邻顶点位于射线
该
动态凸包(不删)
只增动态凸包维护一个数据结构,支持以下两个操作:
- 修改:在当前点集中加入一个点
- 询问:查询一点是否在当前点集的凸包内
分别使用按横坐标有序的结构维护上凸壳和下凸壳,才能完成整个凸包的点内查询。相同横坐标下,上凸壳只保留必要的最高候选,下凸壳只保留必要的最低候选;重复点和边界共线点采用与静态凸包一致的删除策略。
初始化时应单独处理空集、单点和线段。新点位于当前横坐标范围之外时,某一侧可能没有前驱或后继,应按凸壳端点插入处理,不能假设两个邻居始终存在。
- 询问:分别在上下凸壳中二分相邻点,单次最坏时间为
; - 修改:二分找到新点横坐标附近的前驱、后继。若新点不可能成为该凸壳的顶点则舍弃;否则插入,并向左右连续删除所有破坏凸性的旧顶点。
设某次插入共删除 std::set::erase(iterator) 的摊还常数时间删除接口时,本次代价为
一个点一旦从不断扩大的凸包中被删除,之后不会重新成为极点,所以每个点在每条凸壳上至多被删除一次。连续插入
