获得凸包的算法可以算是计算几何中最基础的算法之一了。寻找凸包的算法有很多种,Graham Scan算法是一种十分简单高效的二维凸包算法,能够在O(nlogn)的时间内找到凸包。

首先介绍一下二维向量的叉积(这里和真正的叉积还是不同的):对于二维向量a=(x1,y2)和b=(x2,y2),a×b定义为x1*y2-y1*x2。而它的几何意义就是|a||b|sin<a,b>。如果ab夹角小于180度(逆时针),那么这个值就是正值,大于180度就是负值。需要注意的是,左乘和右乘是不同的。如图所示:

Graham Scan算法的做法是先定下一个起点,一般是最左边的点和最右边的点,然后一个个点扫过去,如果新加入的点和之前已经找到的点所构成的“壳”凸性没有变化,就继续扫,否则就把已经找到的最后一个点删去,再比较凸性,直到凸性不发生变化。分别扫描上下两个“壳”,合并在一起,凸包就找到了。这么说很抽象,我们看图来解释:

我们找下“壳”,上下其实是一样的。首先加入两个点A和C:

然后插入第三个点G,并计算AC×CG的叉积,却发现叉积小于0,也就是说逆时针方向上∠ACG大于180度,于是删去C点,加入G点:

然后就是依照这个步骤便能加入D点。在AD上方是以D为起点。就能够找到AGD和DFEA两个凸壳。合并就得到了凸包。

关于扫描的顺序,有坐标序和极角序两种。坐标序是比较两个点的x坐标,如果小的先被扫描(扫描上凸壳的时候反过来);如果两个点x坐标相同,那么就比较y坐标,小的先被扫描(扫描上凸壳的时候也是反过来)。极角序使用arctan2函数的返回值进行比较,我没写过所以也不是很清楚。
程序可以写得很精简,以下是我用C++写得凸包程序

/*
d[]是一个Point的数组,Point有两个两个属性x和y,同时支持减法操作和det(叉积)。
convex数组保存被选中的凸包的点的编号,cTotal是凸包中点的个数
*/
bool cmpPoint(const Point &a, const Point &b)  //比较坐标序所用的比较函数
{if (a.x!=b.x) return a.x<b.x; return a.y<b.y; } void get_convex_hull() { sort(d,d+N,cmpPoint); int Total=0,tmp; for (int i=0;i<N;++i) //扫描下凸壳 { while ( (Total>1) && ((d[convex[Total-1]]-d[convex[Total-2]]).det( //获得凸包中最后两个点的向量 d[i]-d[convex[Total-1]])<=0) ) Total--; //获得准备插入的点和凸包中最后一点的向量,计算叉积 convex[Total++]=i; } tmp=Total; for (int i=N-2;i>=0;--i) //扫描上凸壳 { while ( (Total>tmp) && ((d[convex[Total-1]]-d[convex[Total-2]]).det( d[i]-d[convex[Total-1]])<=0) ) Total--; convex[Total++]=i; } cTotal=Total; }

我们来看一道题:POJ1113 Wall,题意是给一些点,找一个闭合曲线C,使C能包住所有的点,并且给定的点到C的距离最小为L,问C的周长。稍微画一画就知道这个C的周长是这些点所构成的凸包的周长加上以L为半径的圆的周长。于是求一个凸包再加上2πL就可以了。我的程序如下:

#include <cstdio>
#include <cstring> #include <cstdlib> #include <algorithm> #include <cmath> using std::sort; #define MAXN 1002 int N,L; double sqr(double a) { return a*a; } struct Point { double x,y; inline Point operator- (const Point &t) { Point ret; ret.x=x-t.x; ret.y=y-t.y; return ret; } inline Point operator+ (const Point &t) { Point ret; ret.x=x+t.x; ret.y=y+t.y; return ret; } inline int det(const Point &t) { return x*t.y-t.x*y; } inline double dist(Point &t) { return sqrt(sqr(x-t.x)+sqr(y-t.y)); } }d[MAXN]; bool cmpPoint(const Point &a, const Point &b) { if (a.x!=b.x) return a.x<b.x; return a.y<b.y; } int convex[MAXN],cTotal; void get_convex_hull() { sort(d,d+N,cmpPoint); int Total=0,tmp; for (int i=0;i<N;++i) { while ( (Total>1) && ((d[convex[Total-1]]-d[convex[Total-2]]).det( d[i]-d[convex[Total-1]])<=0) ) Total--; convex[Total++]=i; } tmp=Total; for (int i=N-2;i>=0;--i) { while ( (Total>tmp) && ((d[convex[Total-1]]-d[convex[Total-2]]).det( d[i]-d[convex[Total-1]])<=0) ) Total--; convex[Total++]=i; } cTotal=Total; } int main() { scanf("%d%d",&N,&L); for (int i=0;i<N;++i) { scanf("%lf%lf",&d[i].x,&d[i].y); } get_convex_hull(); double Ans=0; for (int i=0;i<cTotal-1;++i) { Ans+=d[convex[i]].dist(d[convex[i+1]]); } Ans+=d[convex[0]].dist(d[convex[cTotal-1]]); Ans+=3.1415926*2*L; printf("%.0lf\n",Ans); return 0; }

转载于:https://www.cnblogs.com/czaoth/p/6912073.html

Graham Scan凸包算法相关推荐

  1. 凸包Graham Scan算法实现

    凸包Graham Scan算法实现 凸包算法实现点集合中搜索凸包顶点的功能,可以处理共线情况,可以输出共线点也可以不输出而只输出凸包顶点.经典的Graham Scan算法,点排序使用极角排序方式,并对 ...

  2. graham算法 java_凸包Graham Scan算法实现

    凸包算法实现点集合中搜索凸包顶点的功能,可以处理共线情况,可以输出共线点也可以不输出而只输出凸包顶点.经典的Graham Scan算法,点排序使用极角排序方式,并对共线情况做特殊处理.一般算法是将共线 ...

  3. 计算几何入门 1.6:凸包的构造——Graham Scan算法

    上文简要分析出了凸包构造问题算法的下界:O(nlogn),在此就引入一种下界意义上最优的算法:Graham Scan算法.这种算法可以保证在最坏情况下时间复杂度也不超过nlogn.我们先大致了解一下算 ...

  4. 计算几何--凸包之graham scan算法

    Graham scan算法主要步骤: 找出所有已知点的y值最小,如果相同,取x值最小的点,作为基准点s. 以s为基准,所有的点按照与X轴夹角从小到大排序. 使用两个栈,一个记录已访问的点,一个记录未访 ...

  5. Graham Scan算法

    预备知识:凸集:集合S中任意两点的连线都在集合S中,如果简单的理解,可以理解为凸边形 凸包:对于给定集合X,所有包含X的凸集的交集,简单的理解,就是包含X的最小凸集,或者就是最外圈的点连起来 极角排序 ...

  6. 凸包算法Graham扫描法

    凸包算法(Graham扫描法) 转载自 SZUhg https://www.cnblogs.com/wpbing/p/9456240.html 叉乘与线段相交 判断一个点是否在一条线段的左边还是右边, ...

  7. Python:实现graham scan葛立恒扫描法算法(附完整源码)

    Python:实现graham scan葛立恒扫描法算法 from __future__ import annotations from collections import deque from e ...

  8. 凸包算法-------Graham扫描法

    在网络上关于凸包解法多种多样,讲的也非常的不错,可以参考一下这篇博客,但是还是想用自己的话去描述一遍,以加深一下印象. 常见的解法有: 穷举法(蛮力法) 分治法 Jarvis步进法 Graham扫描法 ...

  9. 多边形快速凸包算法(Melkman‘s Algorithm)

    前言 平面点集的凸包算法一文介绍了如何计算平面点集或者任意多边形的凸包.对于随机的平面点集,Graham scan和Andraw's 单调链算法已经是最快的算法了.但是对于没有自相交的封闭的简单多边形 ...

最新文章

  1. 浅谈对主成分分析(PCA)算法的理解
  2. linux 路由器去广告,linux – 如何正确发送路由器广告?
  3. AndroidStudio的几种依赖方式
  4. [转]ExecuteNonQuery()、ExecuteReader()、ExecuteScalar()的简单说明
  5. mysql存储过程查询所有表_mysql存储过程利用游标查询每个数据库的所有表
  6. python语言程序设计赵璐课后答案_Python语言程序设计
  7. js根据数组中对象的多个属性值进行排序
  8. java属于编译_《程序员修炼之道》-读书笔记一-Java到底属于编译型语言还是解释型语言?...
  9. 不得了!这个 AI 让企业家、技术人员、投资人同台“互怼”
  10. 自定义获取焦点的TextView
  11. 蓝色妖姬 t3200 linux连接
  12. 【测绘程序设计】C#将度分秒(° ‘ “)转换度(°)程序实现(附源码)
  13. Hibernate——缓存
  14. 【Java】P1957 口算练习题—(洛谷OJ)
  15. Matlab之数据筛选
  16. Linux上杀毒软件有哪些?
  17. 关于EOSUNION 的商业创业大赛的一点心得
  18. 王大师送福利啦!2021年面试题目汇总
  19. 冲击波超压峰值曲线绘制
  20. Linux系统部署Javaweb环境

热门文章

  1. next.js 无法导出及导出图片无法加载等问题
  2. 用truffle部署智能合约到以太坊网络(truffle+infura)的简易方法
  3. 训练第一个机器学习模型
  4. CentOS学习笔记 - 7. jekins安装 1
  5. Java线程那点事儿
  6. springboot(六):如何优雅的使用mybatis
  7. redis 命令行 操作
  8. Linux—图解rsyslog及通过 Loganalyzer实现集中式日志管控
  9. 管理对象空间——管理存储参数
  10. 《Unix网络编程(第3版)》代码编译的一些问题