凸包
2016-09-28 19:29
169 查看
凸包是计算几何中的基本问题,在介绍凸包算法之前,先来解决几个计算几何中的几个基础算法。
在解析几何中我们知道叉乘的绝对值表示所围成的面积的两倍,而叉乘的值可以用来判断点在向量的左侧还是右侧
点乘的符号在凸包中可以用来判断共线点在向量的中间还是两侧。
算法复杂度O(nh)这是一个与凸包极点的个数相关的算法,先找到一个极点(一般就是LTL)然后从这个极点出发每次找出最右边的一个点,当扫完的时候凸包也就构造出来了。
这是一个经典的算法,基于这个算法,Andrew提出了一个更快数值稳定性更好的算法(PS:我并不知道快在哪儿,稳定在哪儿。。)
这个算法其实思想是和Graham_Scan的朴素思想是想通的,唯一的优点,个人认为是实现起来比较简单,比如排序不需要再用极角来排序了,直接用坐标来排序是比较容易,也是比较简单的。
计算几何基本算法
叉乘
double Cross(Point& p,Point& q,Point& r) { return (q.x-p.x)*(r.y-p.y) - (r.x-p.x)*(q.y-p.y); }
在解析几何中我们知道叉乘的绝对值表示所围成的面积的两倍,而叉乘的值可以用来判断点在向量的左侧还是右侧
点乘
double Dot(int p,int q,int r) { return (P[r].x-P[p].x)*(P[r].x-P[q].x)+(P[r].y-P[p].y)*(P[r].y-P[q].y);//在两点中间,等于0是为了应对从点的情况 }
点乘的符号在凸包中可以用来判断共线点在向量的中间还是两侧。
LTL(lowest-than-left)最下边最左端点
这个是比较简单的int LTL(int n) { int ltl = 0; for(int i=1 ; i<n ; ++i) if(P[ltl].y==P[i].y) { if(P[ltl].x>P[i].x) ltl = i; }else if(P[ltl].y>P[i].y) { ltl = i; } return ltl; }
凸包中的几个简单算法
Jevies-March
void Jevis_March(int n) { ltl = LTL(n); int k = ltl; do{ int s = (0==k?1:0); for(int i =0 ; i<n ; ++i) { if(i!=k&&i!=s )//不在左边并且不在中间更新s { double area2 = Cross(P[k],P[s],P[i]); if(area2<0 || (area2 == 0 && !between(k,s,i)))s = i;//寻找最右点,如果共线则寻找线端点 } } P[k].next = s; k = s; }while(k!=ltl); }
算法复杂度O(nh)这是一个与凸包极点的个数相关的算法,先找到一个极点(一般就是LTL)然后从这个极点出发每次找出最右边的一个点,当扫完的时候凸包也就构造出来了。
Graham_Scan
这是Graham创造的一个O(nlgn)的一个算法。简单的说就是先找到一个极点,然后再把剩下的点把所有的点按照极角的大小排序,这样排出来的void Graham_Scan(long long n) { ltl = LTL(n); swap(P[ltl],P[0]); ltl = 0; sort(P+1,P+n,cmp); vector<int>s; s.push_back(ltl); //应对重合起始点 int next = 1; s.push_back(next); for(int i=next+1; i<n ;) { if(s.size()<2) { s.push_back(i);i++; }else{ double area2 = Cross(P[s[s.size()-2]],P[s[s.size()-1]],P[i]); if(area2>0) { s.push_back(i);i++; }else { s.pop_back(); } } } //串接凸包上的点 for(int i=0 ; i<s.size()-1 ; ++i) P[s[i]].next = s[i+1]; P[s[s.size()-1]].next =s[0]; }
这是一个经典的算法,基于这个算法,Andrew提出了一个更快数值稳定性更好的算法(PS:我并不知道快在哪儿,稳定在哪儿。。)
Andrew
int Andrew(int n) { int m = 0; sort(P,P+n); for(int i=0 ; i<n ; ++i) { while(m>1 && Cross(P[ch[m-2]],P[ch[m-1]],P[i])<=0)m--; ch[m++] = i; } int k = m; for(int i=n-2 ; i>=0 ;--i ) { while(m>k && Cross(P[ch[m-2]],P[ch[m-1]],P[i])<=0)m--; ch[m++] = i; } if(n>1)m--; return m; }
这个算法其实思想是和Graham_Scan的朴素思想是想通的,唯一的优点,个人认为是实现起来比较简单,比如排序不需要再用极角来排序了,直接用坐标来排序是比较容易,也是比较简单的。
相关文章推荐
- 初学算法 - 求凸包的Garham's Scan算法的C++实现
- 【Google Code Jam 2009 round2 problem D】Watering Plants (两圆交点求法详解)
- 计算几何模板
- 计算几何小模板
- BZOJ2829信用卡凸包
- Farthest Point
- 凸包问题 hdu1392 Surround the Trees
- NYOJ 78 圈水池
- HDU 4922 Hello, Your Package! (计算几何+DP)(WA)
- poj 1514&zoj 1185 Metal Cutting(半平面交)
- UVA 10969 Sweet Dream(圆的相交)
- uva 11177 Fighting Against a Polygonal Monster(凸包与圆的面积交)
- POJ1279 && LA2512 Art Gallery(求多边形的核)
- poj 2540 && uva 10084 Hotter Colder(半平面交)
- UVALive 5878 - Shortest Leash 【计算几何】
- 【计算几何】POJ 2318 & POJ 2398
- 【计算几何】POJ 2653
- 【计算几何】POJ 1113
- 计算几何问题汇总--圆与矩形
- [POJ2826]An Easy Problem?!(计算几何)