编程语言
首页 > 编程语言> > Andrew算法求二维凸包-学习笔记

Andrew算法求二维凸包-学习笔记

作者:互联网

 

凸包的概念

首先,引入凸包的概念:

 

 

 大概长这个样子:

 

 

 那么,给定一些散点,如何快速地求出凸包呢(用在凸包上的点来表示凸包)

Andrew算法流程和思想

常见的求凸包的算法有$Graham$和$Andrew$,$Andrew$是$Graham$扫描算法的变种,和$Graham$相比,$Andrew$更快,且更稳定,所以主要讲一下$Andrew$。

 

首先把所有点以$x$坐标为第一关键字,$y$坐标为第二关键字从小到大进行排序,可以肯定第一个点和最后一个点在答案中。

接下来用以下的例子来帮助理解算法流程:

 第一次,把$1$和$2$加入答案中

 

 

尝试把$3$加进去,发现凹进去了,所以把$2$丢掉,把$3$放进去

 

 

 

 

 我们来看看$2$被丢掉,$3$成功上位的原因(凹进去的原因):
发现是斜率(或者...可以总结成叉积?

在下图中$1->2$的斜率大于$1->3$的斜率,又因为之前按$x$递增排序,所以可以说明$2$在$3$的左上,所以是凹进去的。

换句话说,如果加进去这个点(即当前点,记为$i$)和$i-2$号点的斜率小于$i$号点和$i-1$号点的斜率,那么就要把$i-1$号点去掉并加入$i$号点来维护凸包的性质(不让它凹进去)

 

 

 

 

 

接下来加入$4$,$1->4$斜率大于$1->3$斜率,所以$3$不用被丢掉。

 

 

 加入$5$,$3->5$斜率小于$3->4$斜率,所以丢掉$4$,加入$5$

 

 

 

 

 

 

 

加入$6$,一样的理由,一样的操作。($3->6$斜率小于$3->5$斜率,丢掉$5$,加入$6$)

 

 

 

 

 

 然后发现$3$那个地方也凹进去了($1->6$的斜率小于$1->3$的斜率)

所以$3$也要被丢掉,然后只剩下两个点:

(所以写代码的时候要用$while$)

 

 

 接着来,加入$7$:

 

 

 然后是$8$,发现...斜率只小一点点(图没画好,这刁钻的角度,将就看一下吧...),所以$7$要删掉

不过也顺便解决一个共线的问题,共线嘛,很好解决,反正两个点都在凸包上,都不丢就可以了,后面如果那一条线不属于凸包的话,用$while$丢点的时候两个点斜率是一样的,总会被丢出去的。

 

 

 

 

 

 然后是$9$,发现斜率小,所以丢掉$8$:

 

 

 啊哈,然后发现所有点都已经遍历完了,成功达到了$9$,可是凸包还有一半呢。

倒着再来一次就可以求出上面那个盖盖了:

(下面放流程图,不一一解说了(好累),操作是一样的)

 

 (把$7$悄悄地挪了一下位置) 

 

 

 

 (丢掉$6$,发现斜率的关系和正着的那一次都一样,都是小于) 

 

 

 

 

 

 

 

 

 

 

 

 

 这样, 凸包就求出来啦!

按照以上的思路写代码就可以啦。

例题& 板子

 例题: 求凸包的周长:

 1 /*
 2 ID: Starry21
 3 LANG: C++
 4 TASK: fc          
 5 */ 
 6 #include<cstdio>
 7 #include<algorithm>
 8 #include<vector>
 9 #include<cstring>
10 #include<queue>
11 #include<map>
12 #include<iostream>
13 #include<cmath>
14 using namespace std;
15 #define ll long long
16 #define INF 0x3f3f3f3f
17 #define N 10005
18 struct node{
19     double x,y;
20 };
21 node p[N],s[N]/*凸包上的点*/;
22 int n;
23 double dis(node a,node b)
24 {
25     return sqrt(((a.x-b.x)*(a.x-b.x))+((a.y-b.y)*(a.y-b.y)));
26 }
27 bool cmp(node a,node b)
28 {
29     if(a.x==b.x) return a.y<b.y;
30     return a.x<b.x;
31 }
32 double getk(node a,node b)
33 {
34     if(a.x==b.x) return INF;//在一条竖线上 斜率看成无限大 
35     return (b.y-a.y)/(b.x-a.x);
36 }
37 double Andrew()
38 {
39     sort(p+1,p+n+1,cmp);
40     int cnt=0,tot=0;
41     double sum=0.0;
42     for(int i=1;i<=n;i++)
43     {
44         s[++cnt]=p[i];
45         while(cnt>=3&&getk(s[cnt-2],s[cnt])<getk(s[cnt-2],s[cnt-1]))
46             s[cnt-1]=s[cnt],cnt--;
47     }
48     for(int i=1;i<=cnt-1;i++)
49         sum+=dis(s[i],s[i+1]);
50     tot=cnt;
51     cnt=0;
52     for(int i=n;i>=1;i--)
53     {
54         s[++cnt]=p[i];
55         while(cnt>=3&&getk(s[cnt-2],s[cnt])<getk(s[cnt-2],s[cnt-1]))
56             s[cnt-1]=s[cnt],cnt--;
57     }
58     for(int i=1;i<=cnt-1;i++)
59         sum+=dis(s[i],s[i+1]);
60     tot+=cnt;
61     tot-=2;//tot是凸包上点的个数 
62     //printf("%d\n",tot); 
63     return sum;
64 }
65 int main() 
66 {
67     //freopen("fc.in","r",stdin);
68     //freopen("fc.out","w",stdout);
69     scanf("%d",&n);
70     for(int i=1;i<=n;i++)
71         scanf("%lf %lf",&p[i].x,&p[i].y);
72     printf("%.2lf\n",Andrew());
73     return 0;
74 }
Code

 

标签:node,cnt,Andrew,凸包,斜率,二维,include
来源: https://www.cnblogs.com/lyttt/p/11826321.html