博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
学习笔记:二维凸包-Andrew算法
阅读量:5120 次
发布时间:2019-06-13

本文共 2889 字,大约阅读时间需要 9 分钟。

感受到了,被几何搞得晕头转向的感觉

凸包应该算是计算几何中比较基础和常规的算法了。

顺着我的思路来,也许自己日后更好复习。

前置芝士

首先你需要一堆关于平面向量的芝士

斜率总要会吧

首先我们来看一个简单的问题:

一个平面上给你\(n\)个点,计算把\(n\)个点围起来的最小长度。

这是一个很经典的凸包问题。

那么什么是凸包呢?

大概就是题目中所讲述的那样,把给定点包围在内部的最小凸多边形。

怎么求这个东西呢,这里有好几种方法,这里也就随便介绍一下吧。

\(Graham\)扫描法

二话不说,先放图。

5bf166498833b.png

这边就是随便给你的散点图了。

\(Graham\)扫描法是怎么做的呢?

首先我们要找到一个\(y\)最小的点,建立一个坐标系。为了方便,我们再给每个点标号。(那个图有点歪)

5bf16712620b4.png
把其他点都和P6连起来,我们可以把它看成一个个向量。
5bf167c959436.png
对极角的定义:一条向量与\(x\)轴正半轴的夹角(滚去学必修4)
\(∠1\)就是P1的极角啦。(为了好理解,我们还是说线段不说向量吧

C党的福利:C++有个函数可以求极角\(atan2(y,x)\)可以求出坐标为\((x,y)\)的向量的极角。(额,真香

这样,把每个点按照极角从小到大排序。

在这幅图上就是\(P1<P2<P3<P4<P5\)

排好序后你就成功的做完了预处理。

接着再求凸包:

我们就从这个最底下的点(即P6)开始。

每次向右延伸。

比如一开始找到P1,连起来

5bf16ac103ffd.png
找到P2,再连
5bf16b21d59bb.png
找到P3,再连
5bf16b8d70f79.png
咦?好像绿色那条边才是正解。
那不就有问题了吗?
有问题怎么办?
我们把P2踢掉不就行啦。
好了,\(Graham\)差不多就是这样的,你只要判断你连起来的这些东西是不是凸多边形的一部分。
至于怎么判断,我们暂且不说(等下你就知道了),只要知道一下思路就好了,下面才是我们的重点方法。

\(Andrew\)算法

好了,这才是这里的重点内容。

那上面一大堆是干什么的。
\(Andrew\)算法可以说就是\(Graham\)扫描法的改进。
你把上面的思路当成铺垫就是了。
但是,这个铺垫也提供了很好的思路。
好了,不废话了。
想想画个凸多边形是怎么画的。
首先最左边的点和最右边的点肯定要选吧。
假设把最左边的点作为起笔,那不就是绕一圈就好了吗?
对啊,这就是\(Andrew\)算法。
首先我们把笔尖定在左端点,然后往右画,在向左,最后回到原点。
画出来的轨迹类似一个圆。
类似\(Graham\)扫描法,我们同样要判断这个点能不能被选作为凸多边形的顶点。
然后到了最右端之后,我们再从右边往左扫。
回到最左端后,你就画了一个凸多边形了。
好了,重点来了!
怎么判断要不要选这个点呢?
也就是说怎么判断选的这些点是不是凸多边形的一部分?
想想顺着笔画,凸多边形的边都有哪些特点。
看看刚刚这个图
5bf16b8d70f79.png
为什么连\(P1-P3\)而不是把\(P1-P2,P2-P3\)给连了?
很清晰了吧。
就是斜率嘛!
要是\(P2-P3\)的斜率没有\(P1-P3\)大,那它肯定是凹进去的。
那就没有用啦,踢掉!
然后一直找,来回一波,就找到了答案了。
从头到尾都可以用栈实现
看到这里,你可能已经会了\(Garham\)扫描法了。
即使这样,\(Andrew\)算法还是更有用的。
不然为什么要改进?
不过\(Andrew\)算法貌似也说完了。
看到这里,可能还有一个小问题,
如果画圆回来的时候两个点重复使用怎么办?
这其实是很好证的。
看下面这幅图
5bf17eb802511.png
如果当前回来的点选择了紫色的右边那条边,那它一定会被左边那条边覆盖掉。
最终会回到原点
这就保证了\(Andrew\)算法的正确性。
结束了?贴一下代码。
代码可能很丑,而且还有很多没用的东西,懒得删了。

#include
using namespace std;int n;double ans = 0;#define maxn 11000struct spot{ double x,y; bool operator < (const spot &a) const { if(x == a.x) return y < a.y; return x < a.x; } void read() { scanf("%lf%lf", &x, &y); }}a[maxn];#define inf 99999999struct stacks{ int q[maxn],t; void clear(){t = 0;memset(q, 0, sizeof(q));} void push(int x){q[++ t] = x;} int top(){return q[t];} void pop(){-- t;} bool empty(){return t == 0;} int sec_top(){return q[t - 1];}}q;//拒绝STL依赖bool slope(int ax,int b,int c){ return (a[ax].y-a[b].y)*(a[b].x-a[c].x)>(a[ax].x-a[b].x)*(a[b].y-a[c].y);}double dis(spot a,spot b){return sqrt((a.y - b.y) * (a.y - b.y) + (a.x - b.x) * (a.x - b.x));}int main(){ scanf("%d", &n); for(int i = 1;i <= n;i ++) a[i].read(); sort(a + 1,a + n + 1); for(int i = 1;i <= n;i ++) { while(q.t > 1 && slope(i, q.top(), q.sec_top())) q.pop(); q.push(i); } int k = q.t; for(int i = n - 1;i >= 1;i --) { while(q.t > k && slope(i, q.top(), q.sec_top())) q.pop(); q.push(i); } int last = 1; for(int i = 2;i <= q.t;i ++) ans += dis(a[last], a[q.q[i]]),last = q.q[i]; printf("%.2lf", ans);}

最后,忘了说了,这两个算法的复杂度应该都是\(O(nlogn)\)

时间复杂度主要在排序上。

转载于:https://www.cnblogs.com/dwqhca/p/9979335.html

你可能感兴趣的文章
iPhone的解锁、越狱、激活、固件等等是什么意思,有什么分别?(转)
查看>>
CVS
查看>>
为什么匿名内部类参数必须为final类型
查看>>
Codeforces 629 A. Far Relative’s Birthday Cake
查看>>
【框架学习与探究之日志组件--Log4Net与NLog】
查看>>
操作execl
查看>>
UESTC_导弹拦截 2015 UESTC Training for Dynamic Programming<Problem N>
查看>>
工作用到的SQL语句
查看>>
POJ1002 487-3279
查看>>
sqlserver 生成脚本执行创建索引
查看>>
权益保护-产权保护:专利申请
查看>>
【计算机网络】第二章 网络应用(4)
查看>>
pyqt5-QPlainTextEdit普通文本
查看>>
短信验证码js
查看>>
hadoop学习第二天之伪分布模式安装(下)
查看>>
初学微信小程序 TodoList
查看>>
如何在 vuex action 中获取到 vue 实例
查看>>
Windows绘图中的GDI映射模式
查看>>
MYSQL5.7:几个简单的show语句演示
查看>>
vim 把满足条件的数字进行加上一些数字
查看>>