二维凸包

前言

二维凸包。但是笔者学的依托答辩。

算法讲解

定义

首先来了解什么是二维凸包。

对于一个集合D,D中任意有限个点的凸组合的全体称为D的凸包。
或:
对于一个集合D,所有包含D的凸集之交称为D的凸包。
————某个经常出锅的百科
其实通俗地来讲,凸包就是把一个点集最外面的点连接起来构成的 凸多边形,且能够包含所有点。
还有一种描述方法:凸包是指覆盖平面上n个点的最小的凸多边形。

算法

每一种模板都有很多种人类智慧做法。那接下来将由浅入深地介绍几种求凸包地算法。

斜率逼近法

其实算是一种比较好想的思路了。

  1. 首先在所有点中找出来一个 $y$ 值最小的点,记为 $P_i$。
  2. 从 $P_i$ 开始,然后逆时针找与 $P_i$ 相连的边斜率 $k(k>0)$ 最小的点(如果有多个符合条件的点,则取最远的那个)。
  3. 从选出来的 $p_i$ 开始不断重复(2)的方法,直到找到 $m$ 使得 $P_m=P_1$。
    时间复杂度 $O(nm)$($n$ 为点的个数,$m$ 为凸包上的点的数量)。
    但是一旦凸包上的两个点之间的斜率趋于无限大(即几乎和 y 轴平行),还是有局限性。
    那么就有新的算法了。

Jarvis 算法

本质上还是数学构造法。
针对于斜率逼近法的缺陷,可以做出一些改进:
把斜率逼近法(2)步骤中的斜率改为极角,每次选择极角最小的那个点加入凸包,然后从选出来的这个点继续重复此步骤(如果有极角相同的,则选择最远的那个点)。
时间复杂度 $O(nm)$,除了时间复杂度外无明显的缺陷,但是就是这个时间复杂度的缺陷令我们无法接受…

Graham 算法

本质:

Graham 算法维护一个凸壳,通过不断在 凸壳(就是凸包的一部分)中加入新的点和去除影响凸性的点,最后形成 凸包
该算法主要由两部分组成:

  • 排序
  • 扫描
排序

选择所有点中 $y$ 值最小的点,标记为 $P_1$。
然后在剩下的点中按照极角的大小逆时针排序(求极角的函数:atan2(p.x,p.y)),编号为 $P_2\sim n$。

加入凸包

按照排序的顺序枚举每一个点,依次连线,考虑将当前的点加入栈中进行处理:

  • 如果新加入的点与栈顶元素的连线影响了栈中凸包的凸性的话,就弹出栈顶元素。
  • 一直重复以上步骤,最后把当前元素加入栈中。
    这个的正确性可以手玩一下,扫描的复杂度也是 $O(n)$。但是显然不可能这么完美。算上排序的时间复杂度 $O(n\log_n)$,总的时间复杂度为 $O(n\log_n)$。
    下面放一下 洛谷二维凸包模板

Andrew 算法

  • 首先按照横坐标 $x$ 排序,易知最左边和最右边的点都在凸包上。
  • 从第一个点开始遍历,用类似 Graham 算法中的方法往栈中加入新的点,形成下凸壳。
  • 但是我们发现,这样扫描过去只能形成一半的凸包,即下凸壳。那我们就反过来维护,再形成上凸壳。
    扫描的时间复杂度:$O(n)$,排序复杂度:$O(n\log_n)$,那总的时间复杂度:$O(n\log_n)$。
    (因为懒得自己打,随便从网上贺了一篇代码)。

例题

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

模板题,没什么好说的,代码上面就有。

P3829 [SHOI2012] 信用卡凸包

links,一道很典的例题。把题目转化一下:
先不管圆弧部分,那最终的凸包所有的直线部分即为下图绿色部分:
1
将其平移到圆心(矩形顶点)后发现:绿色部分就是所有矩形的顶点构成的凸包:
1
那么对所有的顶点求一个凸包即可。至于圆弧部分,所有的圆弧相加是一个完整的圆,计算即可。

尾声

完了,其他的自己看吧。(