欢迎访问https://blog.csdn.net/lxt_Lucia~~
宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~
关于凸包,之前一直漏掉了这一个,上周末组队赛,听说有一题就是凸包的模板,可惜不会,听说不算很难,正好这周空余的课很少,就顺便搞了8…
目录
- 前言
- 解一:穷举法(蛮力法)
- 解二:分治法
- 解三:Jarvis步进法
- 解四:Graham扫描法
- 解五:Melkman算法
- 快包算法代码(C语言)
- 例题( HDU 3285 )及模板
前言:
首先,什么是凸包?

然后,什么是凸包问题?
然后,什么是极角排序和左转判定?
1)极角排序:就是选取一个最左的点,按y最小,其次x最小来定义,接下来所有的点针对该点的射线,按角度由小到大,若相同
按距离由近到远来排序。
2)左转判定:这个和叉积有关,对于向量p1(x1,y1),p2(x2,y2)如果x1*y2-x2*y1>0,则从p1到p2左转
下面,我们来看一下解决凸包问题的五种方法。
解一:穷举法(蛮力法)

当上式结果为正时,p3在直线 p1p2 的左侧;当结果为负时,p3在直线 p1p2 的右边。
解二:分治法

注意:在步骤一,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意。
解三:Jarvis步进法

注意:
解四:Graham扫描法
如果没看懂的话,咱们再换种描述方式:
(1)选取最下左的点P0。
(2)计算出每个点相对于P0的角度和距离(利用这个来排序)排序。
(3)设点数为n,将p[n-1]和p[0]入栈,判断点集合是否为一条直线(初始k=2表示当前凸包的大小)。
(4)i 从1到 n-1 遍历,对于p[k-1],p[k-2],p[i]若满足左转,将p[i]压入栈。否则就 i– ,k– 。
如果还没懂,那咱们放在动图里看一下。
以下为用Graham扫描法动态求解的过程:

模板代码实现:
int Polygon::Graham(Polygon &con) //别用自己做对象 { int t=0,k=0; Point tmp;//先y最小再x最小 for(i=1; i
0) con.p[k++]=p[i]; else { i--; k--; } } } return con.n=--k; } /* 9 1 4 3 6 5 7 2 2 3 3 5 4 8 3 9 6 7 1 */
解五:Melkman算法

说真的,这个算法网上的资料也少的可怜,先把网上的解释截个图在这里。
下面详细介绍一下这个基于Graham扫描线算法却更加强大的凸包算法 —— Melkman算法
问题:
平面上有一个简单多边形,沿着多边形的边,按照逆时针的顺序给出多边形的顶点的坐标,要求你求出此多边形的凸包。
因此,我现在来介绍一种由Melkman在1987年发明的,可以在O(n)的时间复杂度下求出正确的,简单多边形的凸包的Melkman算法
这个算法是基于Graham扫描线算法的,他们的大体思路相同,但是不同之处在于,Graham使用一个栈来维护凸包,而Melkman使用一个双头队列来维护凸包,每次不仅仅维护队列头的凸性,也维护队列尾的凸性,因此,它得到每时每刻都包含当前考虑过的所有的点的凸包
下面我们模拟一下
对于一个n个点的多边形,我们开一个大小为2*n的deque(即双头队列),设bot为队列尾,top为队列头,bot初始值为n-1,top初始值为n
我们把点1从前插入队列,同时top++,接下来,由于队列里只有一个点1,我们可以直接把2从队列头插入和把2从队列尾插入注意,从现在起每时每刻,队列头和队列尾的点总是同一个,也就是当前凸包里的最后考虑的点
接下来我们插入点3
我们说过,要在队列头和队列尾都维护凸性,如果队列头不是凸的,在队列头弹栈(top–),如果队列尾不是凸的,我们在队列尾弹栈(bot++),我们写程序的时候,在队列头维护一下,然后入栈,之后在队列尾维护一下,然后入栈,所以最后,我们的队列会是3 1 2 3或者3 2 1 3
这是头三个点
我们现在来考虑点4,我们看,队列头和队列正数第二个点所连成的直线,队列尾和队列倒数第二个点所连成的直线,以及剩下的凸包上的边们,会把平面分为5个部分,看图
如果第四个点落在区域II,说明对列头不合法了,在队列头弹栈,如果在区域III,我们要在队列尾进行维护,如果在区域I,我们要把队列的头和尾一起进行维护,如果在区域IV说明这个点是当前所求得的凸包内部的点,明显对答案没有贡献,我们此时直接忽略这个点去看第五个点(上次用Graham跑凸包时的错误可被此情况排除)
那么,如果点落在区域V呢???
并不会落在区域V!
因为我们是个简单多边形,如果在区域V有点,则必然产生自交边,这是违背简单多边形的定义的。
如此进行下去,直到我们将所有的点考虑一遍,此时bot+1到top-1就是凸包上的点
时间复杂度很明显是线性的,因为每个点最多进栈出栈一次
而且此算法还是个在线算法,可以随时接收新的点,并且我最开始塞入队列中的点不必非得在凸包上,相比之下,这个算法比Graham妙得多
来看一下蒟蒻丑到不行的代码 
实现细节:判断在什么区域时,只需要先排除IV区,接着队列头队列尾分别判断即可,无需细究究竟是什么区域
那么就是这样,通过对此博客的阅读,您:
1),明白了求简单多边形凸包时Graham错误的原因
2),学会了目前最优的凸包算法——Melkman算法
3),明白了这个博客创作者是个蒟蒻
如果您第二条收获并没有,建议您再仔细看看,因为我的博客可能是最详细的讲授Melkman算法的了
就是这样希望您有收获
我的号是1145101354 ,欢迎大家加我来讨论问题(请注明来自CSDN)
以上
(还不快夸我可爱QwQ
扩展:
以上讨论的只是二维的凸包,如果延生为三维、多维的凸包问题呢?如何求解?
不过首先,二维凸包可以用来解决围栏问题、城市规划问题、聚类分析等等。但是三维、多维的凸包可能的使用范畴有哪些?
附:快包算法代码(C语言):
#include
#include
int g_result[240][2]; /*getResult()实现功能:以坐标P0(x1,y1)和Pn(x2,y2)为直线,找出pack里面里这条直线最远的点Pmax *并找出直线P0Pmax和PmaxPn的上包,进行递归。 *注:Pack[0][0]存放点的个数,pack[1]开始存放点的坐标。 *全局变量g_result[][]用来存放凸包上的点,即最终所要的答案。同样g_result[0][0]存放的是已找到的点的个数。 / void getResult(int Pack[240][2], int x1, int y1, int x2, int y2) { int i,t,x3,y3,R,Rmax,tmax; int ResultPack[240][2]; ResultPack[0][0] = 0; if(Pack[0][0] <= 1) return; x3 = Pack[1][0]; y3 = Pack[1][1]; R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3; Rmax = R; tmax = 1; for(i=2;i<=Pack[0][0];i++) { x3 = Pack[i][0]; y3 = Pack[i][1]; R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3; if(R >= 0) { t = ++ResultPack[0][0]; ResultPack[t][0] = x3; ResultPack[t][1] = y3; } if(R > Rmax) { Rmax = R; tmax = i; } } if(Rmax <= 0) { for(i=1;i
x2) { x2 = x3; y2 = y3; } } g_result[1][0] = x1; g_result[1][1] = y1; g_result[2][0] = x2; g_result[2][1] = y2; g_result[0][0] += 2; getResult(Point, x1, y1, x2, y2); getResult(Point, x2, y2, x1, y1); printf("\n\n构成凸包的点有:\n"); for(i=1;i<=g_result[0][0];i++) printf("(%d,%d)\n",g_result[i][0],g_result[i][1]); system("pause"); }
例题( HDU 3285 )
Description

Input
Output
For each data set there are multiple lines of output. The first line contains a decimal integer giving the data set number followed by a single space, followed by a decimal integer giving the total number of vertices of the convex hull. The vertices of the convex hull follow, one per line in standard order. Each line contains the decimal integer x coordinate, a single space and the decimal integer y coordinate.
Sample Input
Sample Output
1 10
4 9
7 9
10 6
10 3
9 2
7 1
2 1
1 2
1 5
2 7
2 8
3 9
6 9
12 7
12 4
10 -2
7 -2
1 0
1 3
3 2
1 3
3 1
4 4
1 3
11 2
19 1
2 1
代码:
#include
参考资料:
https://blog.csdn.net/bone_ace/article/details/
https://blog.csdn.net/foreverlin1204/article/details/
https://blog.csdn.net/thewalker88/article/details/
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208539.html原文链接:https://javaforall.net
