凸包知识简要概括(构造凸包)

 什么是凸包,简单来说就是一个凸多边形。

场景

现在有一些点,让你画一个包含所有点的圈,要求图形的周长最短,很明显,用这些点上的一部分点作为节点连城直线是最短的,而最后的图形就是一个凸多边形。

我们如何来找到凸多边形上的点,第一我们可以发现最边缘的点一定位于最后的凸包上,例如y值最小(最大),x值最小(最大)的点,我们可以利用这些点作为我们构造凸包的起点。

首先,对所有点进行排序,找到y值最小的点,如果存在多组y相同,取最左边的点。

找到起点之后,进行极角排序,就是数学上的比较直线的斜率值(k),显然, arctan(k) 就是到x轴的角度,起点与其他的点连线求出角度值,从小到大排序,排序出的第一个点很显然也在凸包上,因为不存在更右的点。

还可以用叉积的方法去判断是否在左在右。

 1 double cross(ff a,ff b,ff c)
 2 {
 3     return (b.x - a.x)*(c.y-a.y)-(c.x - a.x)*(b.y - a.y);
 4 }
 5 bool cmp(ff  a,ff b)
 6 {
 7     double m = cross(f[0],a,b);
 8     if(m>0)
 9     return 1;
10     if(m==0&&dis(f[0],a)-dis(f[0],b)<=0)
11     return 1;
12     return 0;
13 }//用叉积去判断顺序,如果m==0,说明两点在同一直线上,则距离近的排在前面

 

用排好的点一个个判断是不是凸包上的点,现在我们有两个数组,一个g数组存放凸包上的点,一个f数组放排好序的点,

刚开始时,g数组上有两个点,起点和排序后的第一个点。现在点是否属于凸包。

设置一个cnt值,指向g数组的尾节点。

如果下一个点于g[cnt]的连线相对g[cnt]和g[cnt-1]的连线偏左,该点加入g数组,cnt++;

如果偏右,cnt--,该点继续重复比较的步骤,直到找到相对偏左的点(该过程相当于就是删去s数组凸包内的点)

当最后一个点判断完成后,g数组就储存了从起点逆时针一圈凸包上的点

求凸包的周长

顺序枚举g数组上的点累加dis(g[i],g[i+1]),最后再加上dis(g[cnt],g[0])

完整代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 struct ff
 8 {
 9     double x,y;
10     bool operator < (ff b)const
11     {
12         if(y == b.y)
13         return x<b.x;
14         return y<b.y;
15     }
16 }f[100005],g[100008];double xx,yy;
17 double dis(ff a,ff b)
18 {
19     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
20 }
21 double cross(ff a,ff b,ff c)
22 {
23     return (b.x - a.x)*(c.y-a.y)-(c.x - a.x)*(b.y - a.y);
24 }//求叉积
25 bool cmp(ff  a,ff b)
26 {
27     double m = cross(f[0],a,b);
28     if(m>0)
29     return 1;
30     if(m==0&&dis(f[0],a)-dis(f[0],b)<=0)
31     return 1;
32     return 0;
33 }
34 int main() 
35 {
36     int n;
37     scanf("%d",&n);
38     for ( int i = 0;i<n;++i) scanf("%lf%lf",&f[i].x,&f[i].y);
39     sort(f,f+n);//找到最左最下的点
40     if(n == 1)
41     printf("%.2f\n",0.00);
42     else if(n == 2)
43     printf("%.2f\n",dis(f[0],f[1]));
44     else
45     {
46         g[0] = f[0];//凸包数组放入起点
47         xx = g[0].x;
48         yy = g[0].y;
49         sort(f+1,f+n,cmp);//极角排序
50         g[1] = f[1];//第二个点也在凸包内
51         int cnt = 1;//指向凸包数组的尾结点
52         for(int i = 2;i<n;++i)
53         {
54             while(i>=1&&cross(g[cnt-1],g[cnt],f[i])<0)
55             cnt--;//如果新加进来的节点相对最后两点连线偏右,减去最后一个点,循环判断
56             g[++cnt] = f[i];//加入新节点
57         }
58         double s = 0;
59         
60         for (int i = 1;i<=cnt;++i)
61         s+=dis(g[i-1],g[i]);
62         s+=dis(g[cnt],f[0]);//计算每条边的长度
63         printf("%.2lf",s);
64     }
65     return 0;
66 }
View Code

题目:https://www.luogu.com.cn/problem/P2742

借鉴:https://www.cnblogs.com/aiguona/p/7232243.html

posted @ 2020-10-03 23:52  榉橡  阅读(788)  评论(0编辑  收藏  举报