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$,发现斜率的关系和正着的那一次都一样,都是小于)






这样, 凸包就求出来啦!
按照以上的思路写代码就可以啦。
例题& 板子
/*
ID: Starry21
LANG: C++
TASK: fc
*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define N 10005
struct node{
double x,y;
};
node p[N],s[N]/*凸包上的点*/;
int n;
double dis(node a,node b)
{
return sqrt(((a.x-b.x)*(a.x-b.x))+((a.y-b.y)*(a.y-b.y)));
}
bool cmp(node a,node b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
double getk(node a,node b)
{
if(a.x==b.x) return INF;//在一条竖线上 斜率看成无限大
return (b.y-a.y)/(b.x-a.x);
}
double Andrew()
{
sort(p+,p+n+,cmp);
int cnt=,tot=;
double sum=0.0;
for(int i=;i<=n;i++)
{
s[++cnt]=p[i];
while(cnt>=&&getk(s[cnt-],s[cnt])<getk(s[cnt-],s[cnt-]))
s[cnt-]=s[cnt],cnt--;
}
for(int i=;i<=cnt-;i++)
sum+=dis(s[i],s[i+]);
tot=cnt;
cnt=;
for(int i=n;i>=;i--)
{
s[++cnt]=p[i];
while(cnt>=&&getk(s[cnt-],s[cnt])<getk(s[cnt-],s[cnt-]))
s[cnt-]=s[cnt],cnt--;
}
for(int i=;i<=cnt-;i++)
sum+=dis(s[i],s[i+]);
tot+=cnt;
tot-=;//tot是凸包上点的个数
//printf("%d\n",tot);
return sum;
}
int main()
{
//freopen("fc.in","r",stdin);
//freopen("fc.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%lf %lf",&p[i].x,&p[i].y);
printf("%.2lf\n",Andrew());
return ;
}
Code
还有一个用叉积写的,原理都是一样的, 不过我自己不是很喜欢这种写法:
/*
ID: Starry21
LANG: C++
TASK: shuttle
*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define N 10005
struct node{
double x,y;
};
node p[N],s[N]/*凸包上的点*/;
int n;
double dis(node a,node b)
{
return sqrt(((a.x-b.x)*(a.x-b.x))+((a.y-b.y)*(a.y-b.y)));
}
bool cmp(node a,node b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
bool Cross(node a,node b,node c)
{
double x1=a.x-b.x,y1=a.y-b.y;
double x2=c.x-b.x,y2=c.y-b.y;
if((x1*y2-x2*y1)<=) return ;
//如果不希望在凸包的边上有输入点。把<=改成<
return ;
}
int Andrew()
{
sort(p+,p+n+,cmp);
int num=;
for(int i=;i<=n;i++)
{
while(num>&&!Cross(s[num-],s[num-],p[i]))
num--;
s[num++]=p[i];
}
int tmp=num;
for(int i=n-;i>=;i--)
{
while(num>tmp&&!Cross(s[num-],s[num-],p[i]))
num--;
s[num++]=p[i];
}
if(n>) num--;
return num;
}
int main()
{
//freopen("shuttle.in","r",stdin);
//freopen("shuttle.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%lf %lf",&p[i].x,&p[i].y);
int num=Andrew();
double sum=;
for(int i=;i<=num-;i++)
sum+=dis(s[i],s[i+]);
sum+=dis(s[num],s[]);//还有第n个点到第1个点的距离
printf("%.2lf",sum);
return ;
}
Code
Andrew算法求二维凸包-学习笔记的更多相关文章
- 使用Graham扫描法求二维凸包的一个程序
#include <iostream> #include <cstring> #include <cstdlib> #include <cmath> # ...
- python生成个性二维码学习笔记
在linux环境下进行编码 1.先进家目录,自行创建Code文件夹 cd Code 2.下载MyQR库 sudo pip3 install MyQR 3.下载所需资源文件并解压 Code/ $ wge ...
- GiftWrapping算法解决二维凸包问题
一.问题描述 凸集(Convex Set): 任意两点的连线都在这个集合内的集合就是一个凸集. ⒈对于一个集合D,D中任意有限个点的线性组合的全体称为D的凸包. ...
- Codeforces Gym 100286A. Aerodynamics 计算几何 求二维凸包面积
Problem A. AerodynamicsTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/co ...
- java生成二维码学习笔记
纠错等级: QRErrorCorrectLevel.L 7%的字码可被修正 QRErrorCorrectLevel.M 15%的字码可被修正 QRErrorCorrectLevel.Q 25%的字码可 ...
- 计算几何 二维凸包问题 Andrew算法
凸包:把给定点包围在内部的.面积最小的凸多边形. Andrew算法是Graham算法的变种,速度更快稳定性也更好. 首先把全部点排序.依照第一keywordx第二keywordy从小到大排序,删除反复 ...
- Luogu P2742 模板-二维凸包
Luogu P2742 模板-二维凸包 之前写的实在是太蠢了.于是重新写了一个. 用 \(Graham\) 算法求凸包. 注意两个向量 \(a\times b>0\) 的意义是 \(b\) 在 ...
- 【计算几何】二维凸包——Graham's Scan法
凸包 点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内.右图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包. 一组平面上的点, ...
- poj 2079 Triangle (二维凸包旋转卡壳)
Triangle Time Limit: 3000MS Memory Limit: 30000KB 64bit IO Format: %I64d & %I64u Submit Stat ...
随机推荐
- HDU-3613-Best Reward(Manacher, 前缀和)
链接: https://vjudge.net/problem/HDU-3613 题意: After an uphill battle, General Li won a great victory. ...
- react入门及简单配置
直接script引入的不说了 1.npm i -g create-react-app 2.create-react-app my-app (其中my-app为项目名称) 3.进入项目cd my-app ...
- electron-vue 引入OpenLayer 报错 Unexpected token export
electron-vue 引入OpenLayer 报错 Unexpected token export 解决办法: 在 .electron-vue/webpack.renderer.config.js ...
- Ubuntu完全删除nginx
1.删除nginx,-purge包括配置文件 sudo apt-get --purge remove nginx 2.移除全部不使用的软件包 sudo apt-get autoremove 3.罗列出 ...
- 创建虚拟机,安装操作系统,xshell6远程链接
一.创建虚拟机 1. 首先安装vmware,注意在安装中,下面的两项不要勾选,一路下一步 2.完成安装打开之后,创建新的虚拟机 3.虚拟机创建完成,需要改配置 4.然后设置网段 5.查看服务,在运行状 ...
- [Python] 等号赋值, copy, deepcopy的区别
参考链接: 1. 介绍python中的可变类型与不可变类型:https://blog.csdn.net/answer3lin/article/details/86430074 (也可以参考转载博客 P ...
- error while loading shared libraries: libXXXX.so: cannot open shared object file: No such file or directory
出现这个问题的原因是运行程序缺少依赖库,或者运行程序的依赖库缺少依赖库,可能你的本地目录下面就有这个库文件,但是linux搜索路劲不会从当前路径下去搜索:这种情况可能出现在切换环境上,可能你在一个li ...
- [译]Webpack 4 — 神秘的SplitChunksc插件
原文链接:Webpack 4 - Mysterious SplitChunks Plugin 官方发布了 webpack 4,舍弃了之前的 commonChunkPlugin,增加了 SplitChu ...
- LeetCode 143. 重排链表(Reorder List)
题目描述 给定一个单链表 L:L0→L1→…→Ln-1→Ln , 将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→… 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. ...
- 如何安装Vscode软件及设置成中文界面?
最新小编开始研究前端,想写点东东玩,要想开始首先就要安装工具,写前端有好几个工具,安装推荐开源的vscode,第一次安装是英文版的,如何修改成中文版,也是很简单的,跟随小编一起来安装设置工具吧. 方法 ...