计算几何——凸包问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算几何——凸包问题相关的知识,希望对你有一定的参考价值。
注:本文是2016年春季清华大学邓俊辉老师《计算几何》MOOC课程的简要个人总结系列之一,我将同步课程内容更新。不过有可能写的不完全是课程内容,也包含一些个人理解。如果你在看完本文后开始对计算几何感兴趣,请前往相应的MOOC平台完整学习邓老师的课程。如此精心设计和编排的课程,不应该被辜负。在此感谢邓老师!
Knowledge Dependence:阅读本文前你只需要有基本的几何知识和算法知识即可。代码实现需要一丢丢C++基础。
由于作者很懒不想画图,所以你还需要一点脑内小剧场。不过相信我,自己在脑中一步一步地形象化问题将会很有意思。不信?往下看咯。
极边(Extreme Edge)算法
在上一篇中,我们把注意力放在了极点上,并得出了一个时间复杂度高达O(n^4)的算法,这是不能接受的。
现在,我们转换我们的注意力,来关注极边。
什么是极边?也很简单:组成凸包的边就是极边。
那我们如何利用极边来求解凸包呢?跟极点算法一样,我们同样来考查极边的特点。
对于任意两极点所组成的线段(其中包括我们要找的「极边」),我们可以明显地发现,极边右侧一定是空的(同上一篇,规定逆时针方向为正方向)!也就是是说,对于给定的点集,极边右侧是没有点的!而对于不是极边的那些有向线段,我们也可清晰地发现它的两侧都肯定不是空的。
基于极边的这个特点,我们可以自然地得到这样的找极边算法:
首先枚举给定点集的任意两点组成的「有向线段」(Directed Edge),然后对于当前的这条有向线段,我们再枚举所有点(除组成有向线段顶点的两点),并一一判断该点是否在这条有向线段的右侧,如果在,那就说明我们当前枚举到的这条有向线段并不是我们要找的极边。
同样值得一提的是,在具体实现的过程中,由于我们是枚举任意两点来确定枚举的有向线段,但「有向」这件事并不是那么好确定的。不过,好在实际上,我们并不需要严格地判别点是不是在该有向线段的「右」侧,而只需要判别出该有向线段的「某一」侧是否为空的即可。
那如何判断点在有向线段的哪一侧呢?
好熟悉的问题啊!没错,上一篇中我们同样地曾经把问题化归成了这个子问题!答案就是利用2倍有向面积来做 ToLeft Test!
至此,算法已经清晰明了。
具体的代码实现如下,代码同样十分清晰,我依然建议你跟着代码再清楚地梳理一遍:
1 /******************************* 2 * Extreme_Edge_Algorithm for Convex Hull construction 3 * Time Complexity: O(n^3) 4 ********************************/ 5 6 void markEE(Point S[], int n) { 7 // 初始化:“有罪推论”,即预设所有点都不是极点(也就是所有有向线段都不是极边) 8 for (int k = 0; k < n; k++) S[k].extreme = false; 9 // 枚举所有的“有向边” 10 for (int p = 0; p < n; p++) 11 for (int q = p + 1; q < n; q++) 12 checkEdge(S, n, p, q); // directed edge pq 13 } 14 15 void checkEdge(Point S[], int n, int p, int q) { 16 // 初始化:先假设有向边 pq 两边都都空的(即没有点) 17 bool LEmpty = true, REmpty = true; 18 // 枚举所有除 p, q 外的点,并对其与有向边 pq 进行 ToLeft Test 19 for (int k = 0; k < n && (LEmpty || REmpty); k++) { 20 if (k != p && k != q) { 21 // 如果 ToLeft Test 为真则左边不为空,否则右边不为空 22 ToLeft(S[p], S[q], S[k]) ? LEmpty = false : REmpty = false; 23 } 24 } 25 // 如果有向边 pq 有一侧为空,则 pq 即为极边,组成极边的顶点即可标记为 26 if (LEmpty || REmpty) 27 S[p].extreme = S[q].extreme = true; 28 } 29 30 bool ToLeft(Point p, Point q, Point s) { 31 // 将 ToLeft Test 等价为求2倍“方向面积”,避免除法或三角运算带来的精度问题 32 return Area2(p, q, s) > 0; 33 } 34 35 int Area2(Point p, Point q, Point s) { 36 // 2倍“有向面积” 37 return 38 p.x * q.y - p.y * q.x 39 + q.x * s.y - q.y * s.x 40 + s.x * p.y - s.y * p.x; 41 }
同样地,我们来分析算法的复杂度。
首先我们枚举有向线段是一个二重循环,然后对于每一个有向线段需要枚举一遍所有的点来进行 ToLeft Test,而 ToLeft Test 是常数时间的。
是的,极边算法是的时间复杂度是 O(n^3) 的。
尽管相对于上一篇的极点算法,我们已经把时间复杂度降低了一个数量级,然后这样的复杂度我们依然是难以接受的,我们依然需要一个新的角度来寻找更优的算法。
【To Be Continued】
以上是关于计算几何——凸包问题的主要内容,如果未能解决你的问题,请参考以下文章
[HDU4316]Mission Impossible(计算几何/凸包/半平面交)
P2742 [USACO5.1]圈奶牛Fencing the Cows /模板二维凸包(计算几何)(凸包)