一直听大佬们说:凸包、凸包、凸包
一直不会。。。。。
而后。。。。
今天考试,考了一道计算几何的简单题。。。。
这,,,仍是学一下吧。。
而后考试现场学习一下凸包算法。算法
先理解一下凸包是啥东西。函数
看看这张图
解释一下凸包是什么
若是你有一堆点(原谅我画的很凌乱)
那么,找到一个点集
依次链接这些点
使他们造成一个凸多边形
而且全部的点都包括在这个多边形的内部或者边上
这个多边形就是一个凸包(我写的确定一点也不严谨)学习
无论怎么样,就先这样理解一下吧。。。。。。.net
凸包是啥应该不难理解,那么,给你一堆点,怎么求凸包?code
这种东西。。。。。
先大概说一下把。。。blog
首先找到最靠近左下的那个点,这个点必定在凸包上(不难理解吧。。。画个图就知道了)排序
以这个点为极点,其余点按照极角排序图片
而后按照顺序依次访问全部点,判断可行性get
这样子干说真是虚无缥缈的东西。。。。。。
画图来解释class
这是一片点。
找到最靠近左下的一个点
其余的点按照极角排序
而后把1丢到凸包的栈里面,准备开始扫描
检查2号点是否在1的一侧,(检查一下是否是凸多边形)
这里检查到2号可行,先加入到栈中
检查到3更加靠近外侧(若是加入3号就会造成凹多边形,显然3在凸包中,而2不在)
而后把2号点弹出栈,判断1号和3号节点的关系(同判断2号)
依次这么判断,最后全部凸包上的点都会在栈中
这样子算法的步骤很显然了。
继续解决一些细节上的问题(貌似就一个把。。。。)
怎么计算一个节点是否在前一个点的一侧。。。。
(我说的好不专业。。。我本身都不知道该怎么说一些名词。。。就将就着理解一下吧。。。)
咱们先拿几个点出来
其中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
这道题目就是求出凸包而后计算周长,很简单的题目,去试试吧。。