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 }
View Code

 

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 }
View Code

 

 

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 }
View Code
 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)

Opencv Convex Hull (凸包)

凸包(Convex Hull)构造算法——Graham扫描法

OpenCV入门之寻找图像的凸包(convex hull)

Gym 101986D Making Perimeter of the Convex Hull Shortest(凸包+极角排序)

[ABC202F]Integer Convex Hull