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

先理解一下凸包是啥东西。

凸包--Graham扫描法

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

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


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

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

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

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

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

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

凸包--Graham扫描法

这是一片点。

凸包--Graham扫描法

找到最靠近左下的一个点

凸包--Graham扫描法

其他的点按照极角排序

凸包--Graham扫描法

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

凸包--Graham扫描法

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

凸包--Graham扫描法

检查到3更加靠近外侧(如果加入3号就会形成凹多边形,显然3在凸包中,而2不在)
然后把2号点弹出栈,判断1号和3号节点的关系(同判断2号)

凸包--Graham扫描法

依次这么判断,最后所有凸包上的点都会在栈中

凸包--Graham扫描法


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

我们先拿几个点出来

凸包--Graham扫描法

其中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 A<B;
	   else    return a.x<b.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].x<p[0].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

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

相关文章: