Convex hull凸包
Posted 指尖泛出的繁华
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Convex hull凸包相关的知识,希望对你有一定的参考价值。
把一个平面上给出的点都包含进去的最小凸多边形。逆时针输出凸包的各个顶点。
1.Graham扫描法 (O(n*logn))-------旋转扫除的技术:
2.Jarvis march步进法(O(n*h))h为凸包的顶点数--------打包的技术
应用:求二维平面最远点对。
uva,109
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 5 using namespace std; 6 7 struct node 8 { 9 double x,y; 10 //便于求出初始点 11 bool operator<(const node& a)const{ 12 return x<a.x || (x==a.x && y<a.y); 13 } 14 }; 15 16 17 typedef vector<node> Vnode;//凸包 18 typedef vector<Vnode> VVnode;//凸包个数 19 20 //叉积判断是否向左转。 21 double Isleft(node& p0,node& p1,node& p2) 22 { 23 return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); 24 } 25 26 //求凸包 27 int Convex_hull(Vnode p,Vnode &h){ 28 int n=p.size(); 29 int k=0; 30 //按x升序然后y升序排列 31 sort(p.begin(),p.end()); 32 33 for(int i=0;i<n;i++){ 34 //上链 35 while(k>=2 && Isleft(h[k-2],h[k-1],p[i])<=0) 36 k--; 37 h[k++]=p[i]; 38 } 39 //下链 40 for(int i=n-2,t=k+1;i>=0;i--){ 41 while(k>=t && Isleft(h[k-2],h[k-1],p[i])<=0) 42 k--; 43 h[k++]=p[i]; 44 } 45 return k; 46 } 47 //检查是否在凸多边形内 48 int check(Vnode h, node p) { 49 int n = h.size(); 50 int i, j, c = 0; 51 for (i = 0, j = n-1; i < n; j = i++) 52 if ((((h[i].y <= p.y) && (p.y < h[j].y)) || ((h[j].y <= p.y) && (p.y < h[i].y))) && (p.x < (h[j].x - h[i].x) * (p.y - h[i].y) / (h[j].y - h[i].y) + h[i].x)) 53 c = !c; 54 return c; 55 } 56 //根据题意求凸包面积 57 double Area(Vnode p){ 58 int n=p.size(); 59 double s=0; 60 61 for(int i=1;i<n;i++){ 62 s+=((p[i-1].x*p[i].y-p[i].x*p[i-1].y))/2; 63 } 64 return s; 65 } 66 67 68 int main() 69 { 70 int n; 71 VVnode hull; 72 73 while(cin>>n && n!=-1) 74 { 75 Vnode p(n); 76 for(int i=0;i<n;i++) 77 cin>>p[i].x>>p[i].y; 78 79 Vnode h(n+1); 80 n=Convex_hull(p,h); 81 h.resize(n); 82 hull.push_back(h); 83 } 84 85 node k; 86 double a=0; 87 while(cin>>k.x>>k.y){ 88 VVnode::iterator it; 89 for(it=hull.begin();it<hull.end();it++) 90 { 91 if(check(*it,k)) 92 { 93 a+=Area(*it); 94 hull.erase(it); 95 break; 96 } 97 } 98 } 99 //输出时带两位小数 100 cout.precision(2); 101 //fixed固定格式,floatfield代表以浮点数输出 102 cout.setf(ios::fixed,ios::floatfield); 103 cout<<a<<endl; 104 105 return 0; 106 }
uva,10002
对于给定点对,求出凸包然后求其重心。
重心=(每个三角形重心*有向面积)/总面积/3;
一般三角形以源点(0,0)为起点比较方便。注意三角形重心 ((x1+x2+x3)/3, (y1+y2+y3)/3)
所以求三角形重心用矢量加法/3则得重心。面积用叉积求/2.
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 6 using namespace std; 7 const int maxn=105; 8 const double eps=1e-10; 9 10 struct node 11 { 12 double x,y; 13 node(){} 14 node(double x,double y):x(x),y(y){} 15 16 bool operator<(const node& p)const{ 17 return x<p.x || (x==p.x && y<p.y); 18 } 19 //定义函数简单方便 20 node operator+(node p){ 21 return node(x+p.x,y+p.y); 22 } 23 24 node operator-(node p){ 25 return node(x-p.x,y-p.y); 26 } 27 28 node operator*(double d){ 29 return node(x*d,y*d); 30 } 31 32 node operator/(double d){ 33 return node(x/d,y/d); 34 } 35 36 double det(node p){ 37 return x*p.y-y*p.x; 38 } 39 }ans[maxn],a[maxn]; 40 41 42 int n,len; 43 44 void Convex_hull() 45 { 46 sort(a,a+n); 47 len=0; 48 int i; 49 for(i=0;i<n;i++) 50 { 51 while(len>1 && (ans[len-1]-ans[len-2]).det(a[i]-ans[len-1])<=eps) len--; 52 ans[len++]=a[i]; 53 } 54 55 int tmp=len; 56 for(i=n-2;i>=0;--i){ 57 while(len>tmp && (ans[len-1]-ans[len-2]).det(a[i]-ans[len-1])<=eps) len--; 58 ans[len++]=a[i]; 59 } 60 --len; 61 } 62 //求多边形面积 63 double Area() 64 { 65 double sum=0.0; 66 67 for(int i=0;i<len;i++) 68 sum+=ans[i].det(ans[i+1]); 69 return sum/2.0; 70 } 71 //求重心 72 node masscenter() 73 { 74 node res=node(0,0); 75 double s=Area(); 76 if(s<eps) return res; 77 78 for(int i=0;i<len;i++) 79 res=res+(ans[i]+ans[i+1])*(ans[i].det(ans[i+1])); 80 return res/s/6.0; 81 } 82 83 int main() 84 { 85 int i; 86 node res; 87 while(scanf("%d",&n) && n>=3){ 88 for(i=0;i<n;i++) scanf("%lf%lf",&a[i].x,&a[i].y); 89 90 Convex_hull(); 91 res=masscenter(); 92 printf("%.3lf %.3lf\\n",res.x,res.y); 93 } 94 return 0; 95 }
10078
一开始是想的求点阵的凸包然后比较先后的点数,如果点减少则说明存在凹多边形所以就yes了。
其实考虑欠周到,还要考虑点是否在凸包里面= =,修正:因为可能他原来就是个凸的,但是有几点共线了。
p1Xp2为正数说明,p1在p2的顺时针方向。否则在逆时针方向。二分查找到顺时针方向的最近点,然后判断是否在线段的左侧就可以了。附带详解:
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 6 using namespace std; 7 const int maxn=55; 8 const double eps=1e-10; 9 10 struct node 11 { 12 double x,y; 13 node(){} 14 node(double x,double y):x(x),y(y){} 15 16 bool operator<(const node& p)const{ 17 return x<p.x || (x==p.x && y<p.y); 18 } 19 20 node operator+(node p){ 21 return node(x+p.x,y+p.y); 22 } 23 24 node operator-(node p){ 25 return node(x-p.x,y-p.y); 26 } 27 28 node operator*(double d){ 29 return node(x*d,y*d); 30 } 31 32 node operator/(double d){ 33 return node(x/d,y/d); 34 } 35 36 double det(node p){ 37 return x*p.y-y*p.x; 38 } 39 }ans[maxn],a[maxn]; 40 41 42 int n; 43 44 int Convex_hull() 45 { 46 if(n<3) return n; 47 sort(a,a+n); 48 int len=0; 49 int i; 50 for(i=0;i<n;i++) 51 { 52 while(len>1 && (ans[len-1]-ans[len-2]).det(a[i]-ans[len-1])<=eps) len--; 53 ans[len++]=a[i]; 54 } 55 56 int tmp=len; 57 for(i=n-2;i>=0;--i){ 58 while(len>tmp && (ans[len-1]-ans[len-2]).det(a[i]-ans[len-1])<=eps) len--; 59 ans[len++]=a[i]; 60 } 61 return --len; 62 } 63 64 double cross(node p0, node p1, node p2) 65 { 66 return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); 67 } 68 //二分算法判断是否在凸多边形内。 69 bool Compl_inside_convex(const node & p,node *con,int n) 70 { 71 if(n<3) return false; 72 if(cross(con[0],p,con[1])>-eps) return false; 73 if(cross(con[0],p,con[n-1])<eps) return false; 74 75 int i=2,j=n-1; 76 int line=-1; 77 78 while(i<=j) 79 { 80 int mid=(i+j)>>1; 81 if(cross(con[0],p,con[mid])>-eps) 82 { 83 line=mid; 84 j=mid-1; 85 } 86 else i=mid+1; 87 } 88 return cross(con[line-1],p,con[line])<-eps; 89 } 90 91 int main() 92 { 93 int i; 94 node res; 95 while(scanf("%d",&n),n){ 96 for(i=0;i<n;i++) scanf("%lf%lf",&a[i].x,&a[i].y); 97 98 int count=Convex_hull(); 99 int flag=0; 100 for(int i=0;i<n;i++) 101 //如果在凸多边形内则原多边形为凹多边形。 102 if(count>2 && Compl_inside_convex(a[i],ans,count)) 103 { 104 flag=1;break; 105 } 106 if(flag) printf("Yes\\n"); 107 else printf("No\\n"); 108 } 109 return 0; 110 }
1 double cross(cpoint p0, cpoint p1, cpoint p2) 2 { 3 return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); 4 } 5 6 bool Compl_inside_convex(const cpoint & p,cpoint *con,int n) 7 { 8 if(n<3) return false; 9 if(cross(con[0],p,con[1])>-eps) return false; 10 if(cross(con[0],p,con[n-1])<eps) return false; 11 12 int i=2,j=n-1; 13 int line=-1; 14 15 while(i<=j) 16 { 17 int mid=(i+j)>>1; 18 if(cross(con[0],p,con[mid])>-eps) 19 { 20 line=mid; 21 j=mid-1; 22 } 23 else i=mid+1; 24 } 25 return cross(con[line-1],p,con[line])<-eps; 26 }
uva,681
注意精度问题,当两者相减的时候考虑极小的精度差,然后就是输入输出。。。见了你的鬼了。。
我一开始就光顾着把核心东西写出来了。。后面提交了一次才发现还是前面一道题目的条件。然后就wa了一下午。
1 #include <iostream> 2 #include <algorithm> 3 #include <cmath> 4 #include <cstdio> 5 #include <vector> 6 #include <cstdlib> 7 #include <cstring> 8 9 using namespace std; 10 #define eps 1e-10 11 12 double add(double a,double b) 13 { 14 if(abs(a+b)<eps*(abs(a)+abs(b))) return 0; 15 return a+b; 16 } 17 18 struct node 19 { 20 double x,y; 21 22 node operator-(node p){ 23 return node(add(x,-p.x),add(y,-p.y)); 24 } 25 26 double det(node p){ 27 return add(x*p.y,-y*p.x); 28 } 29 30 node(){} 31 node(double x,double y):x(x),y(y){} 32 }; 33 34 bool cmp(const node& a,const node& b) 35 { 36 if(fabs(a.y-b.y)>eps) 37 return a.y<b.y; 38 return a.x<b.x; 39 } 40 41 vector<node> convex_hull(vector<node> ps,int n) 42 { 43 sort(ps.begin(),ps.end(),cmp); 44 45 int k=0; 46 vector<node> qs(n*2); 47 48 for(int i=0;i<n;i++){ 49 while(k>1 && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-2])<=0) k--; 50 qs[k++]=ps[i]; 51 } 52 53 for(int i=n-2,t=k;i>=0;i--){ 54 while(k>t && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-2])<=0) k--; 55 qs[k++]=ps[i]; 56 } 57 qs.resize(k); 58 return qs; 59 } <以上是关于Convex hull凸包的主要内容,如果未能解决你的问题,请参考以下文章
R语言为散点图添加凸包(convex hull):数据预处理(创建一个包含每组数据凸包边界的数据集)ggplot2使用geom_polygon函数为可视化图像添加凸包(convex hull)
凸包(Convex Hull)构造算法——Graham扫描法
Gym 101986D Making Perimeter of the Convex Hull Shortest(凸包+极角排序)