凸包--Graham扫描法

一直听大佬们说:凸包、凸包、凸包
一直不会。。。。。
然后。。。。
今天考试,考了一道计算几何的简单题。。。。
这,,,还是学一下吧。。
然后考试现场学习一下凸包算法。

先理解一下凸包是啥东西。
凸包--Graham扫描法_第1张图片
看看这张图
解释一下凸包是什么
如果你有一堆点(原谅我画的很凌乱)
那么,找到一个点集
依次连接这些点
使他们形成一个凸多边形
并且所有的点都包括在这个多边形的内部或者边上
这个多边形就是一个凸包(我写的肯定一点也不严谨)

不管怎么样,就先这样理解一下吧。。。。。。


凸包是啥应该不难理解,那么,给你一堆点,怎么求凸包?

这种东西。。。。。
先大概说一下把。。。

首先找到最靠近左下的那个点,这个点一定在凸包上(不难理解吧。。。画个图就知道了)

以这个点为极点,其他点按照极角排序

然后按照顺序依次访问所有点,判断可行性

这样子干说真是虚无缥缈的东西。。。。。。
画图来解释

凸包--Graham扫描法_第2张图片

这是一片点。
凸包--Graham扫描法_第3张图片

找到最靠近左下的一个点

凸包--Graham扫描法_第4张图片

其他的点按照极角排序

凸包--Graham扫描法_第5张图片

然后把1丢到凸包的栈里面,准备开始扫描

凸包--Graham扫描法_第6张图片

检查2号点是否在1的一侧,(检查一下是不是凸多边形)
这里检查到2号可行,先加入到栈中

凸包--Graham扫描法_第7张图片

检查到3更加靠近外侧(如果加入3号就会形成凹多边形,显然3在凸包中,而2不在)
然后把2号点弹出栈,判断1号和3号节点的关系(同判断2号)
凸包--Graham扫描法_第8张图片
依次这么判断,最后所有凸包上的点都会在栈中
凸包--Graham扫描法_第9张图片


这样子算法的步骤很显然了。
继续解决一些细节上的问题(貌似就一个把。。。。)
怎么计算一个节点是否在前一个点的一侧。。。。
(我说的好不专业。。。我自己都不知道该怎么说一些名词。。。就将就着理解一下吧。。。)

我们先拿几个点出来
凸包--Graham扫描法_第10张图片
其中1,2,3是当前在凸包的栈中的点,4号节点是需要判断的点
那么,我们需要从栈中拿最上方的两个点(2和3节点)
把他们连接起来,再把2和4连接起来(怎么连接?我是不会说直接用向量的坐标表示就可以了)
计算一下两个向量的叉积。。
哈,叉积。。。
解释一下吧。。
假设2到3的向量是a(x1,y1)
2到4的向量是b(x2,y2)
那么,计算一下它们的叉积,也就是x1y2-x2y1
换种方法来表示就是。
|a|·|b|·sin<a,b>
(所以说叉积也可以用来求出三角形的面积~这个以后还会用到的)
如果,这两个向量的叉积≥0 证明这两个向量平行或者夹角是个锐角
也就证明了3号节点此时一定不再凸包上(因为连接2和4之后3在凸包内侧了)
把3号节点弹出栈,继续重复上面的步骤即可。


感觉我说的有点小复杂诶。。。。
这个东东多画点图就会理解的

如果还是不太清楚,可以看一看代码。

struct Node
{
       int x,y;
}p[MAX],S[MAX];//p储存节点的位置,S是凸包的栈 
inline bool cmp(Node a,Node b)//比较函数,对点的极角进行排序 
{
       double A=atan2((a.y-p[1].y),(a.x-p[1].x));
       double B=atan2((b.y-p[1].y),(b.x-p[1].x));
       if(A!=B)return Aelse    return a.x//这里注意一下,如果极角相同,优先放x坐标更小的点 
}
long long Cross(Node a,Node b,Node c)//计算叉积 
{
       return 1LL*(b.x-a.x)*(c.y-a.y)-1LL*(b.y-a.y)*(c.x-a.x);
}
void Get()//求出凸包 
{
       p[0]=(Node){INF,INF};int k;
       for(int i=1;i<=n;++i)//找到最靠近左下的点 
              if(p[0].y>p[i].y||(p[0].y==p[i].y&&p[i].x0].x))
               {p[0]=p[i];k=i;}
       swap(p[k],p[1]);   
       sort(&p[2],&p[n+1],cmp);//对于剩余点按照极角进行排序 
       S[0]=p[1],S[1]=p[2];top=1;//提前在栈中放入节点 
       for(int i=3;i<=n;)//枚举其他节点 
       {
              if(top&&Cross(S[top-1],p[i],S[top])>=0)
                        top--;//如果当前栈顶不是凸包上的节点则弹出 
              else  S[++top]=p[i++];//加入凸包的栈中 
       }
       //底下这个玩意用来输出凸包上点的坐标 
       //for(int i=0;i<=top;++i)
       //    printf("(%d,%d)\n",S[i].x,S[i].y);
}

接下来找一道简单点的例题
HDU 1392

这道题目就是求出凸包然后计算周长,很简单的题目,去试试吧。。

你可能感兴趣的:(======总结======)