凸包详解

凸包详解首先讲解一下凸包的概念用比较抽象的说就是 在一个实数向量空间 V 中 对于给定集合 X 所有包含 X 的凸集的交集 S 被称为 X 的凸包 X 的凸包可以用 X 内所有点 X1 Xn 的凸组合来构造 简单来说 给你一个点集 Q 你可以把 Q 中的每个点想象成一块木板上的铁钉 而点集 Q 的凸包就是包围了所有铁钉的一条拉紧了橡皮绳所构成的形状 如图 求凸包可以用 Craham 扫描法 它用了一种

首先讲解一下凸包的概念

用比较抽象的说就是:

在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点

(X1,…Xn)的凸组合来构造.

简单来说:

给你一个点集Q,你可以把Q中的每个点想象成一块木板上的铁钉,而点集Q的凸包就是包围了所有铁钉的一条拉紧

了橡皮绳所构成的形状;

如图:

凸包详解

求凸包可以用Craham扫描法,它用了一种叫”旋转扫除“的技术;

在讲解这个算法之前,我先来介绍几个计算几何概念;

—————————————————————————————————————————

叉积:(在这里只讨论在二维平面的时候)

在二维平面中存在向量p1和p2,他们的叉积等于由向量p1和向量p2构成的平行四边形的面积。

凸包详解

 

若p1为(x1,y1),p2为(x2,y2);那么p1 * p2 = x1 * y2 – x2 * y1;(注意顺序不能变);

叉积的性质:

1:得到两个向量之间的顺逆关系:

若p1 * p2 >0,则p1在p2的顺时针方向;

若p1 * p2 <0,  则p1在p2的逆时针方向;

若p1 * p2 = 0, 则p1和p2共线(有可能同向,有可能反向);

如上图,p1 * p2 > 0,则p1在p2的顺时针时针方向;

 

2:确定一个角的转向:

凸包详解

 

如图有两角

为了好区分我们把第一个角叫做

为了判断角的转向,我们需要判断向量p0p1在p0p2的逆时针方向还是顺时针方向(如图)

如果p0p1在p0p2的顺时针方向,则角是向左转,如

如果p0p1在p0p2的逆时针方向,则角是向右装,   如

总结一下就是对于角

                                              如果p0p1 * p0p2 > 0,则该角向右转;

注意p0必须在最下面(也就是p0的y坐标必须是三个点中最小的)

 

基于这个基本的规律我们就能求出一个点集的凸包

以下是求凸包的过程。

————————————————————————————————————

观察凸包我们能发现凸包上的每一个点与它相邻点构成的角都是向左转的;

我们把相邻的左转角连成的不封闭图形叫做凸壳(如下图)

凸包详解

图一,图二是凸壳,图三不是凸壳。

可以看出求凸包的过程就是一个不断维护凸壳的过程。

我们可以借助栈这个结构来维护凸壳。

第一步我们要对这些点按极角的大小进行排序

        我们首先选择最左下的那个点作为原点(参照点),之后按极角大小排序,(小的在前)

        如果极角相等的话就按距离远近排序,(近的在前);

二步用栈维护这个凸壳,直到形成一个封闭图形为止

代码如下:

#include 
  
    #include 
   
     #include 
    
      using namespace std; const int INF = 50005; struct node { double x,y; }; node data[INF]; node res[INF]; //数组模拟栈(这里如果用stl自带的栈会比较麻烦) double Graham(node* data,node* res, int n); double cross(node a, node b, node mark); //求向量mark->a, 向量mark-> b的叉积; double dis(node a, node b); //求a,b两点距离; long long dis1(node a, node b); bool cmp(node a,node b) { double ans = cross(a,b,data[0]); if(ans > 0 || (ans == 0 && dis(a,data[0]) < dis(b,data[0]))) return true; return false; } /* 8 0 0 2 0 4 0 4 2 4 4 2 4 0 4 0 2 4 0 1 0 2 0 3 0 4 */ int main() { int n; scanf("%d", &n); //点集的大小 for(int i = 0; i < n; i++) scanf("%lf %lf",&data[i].x,&data[i].y); int len = Graham(data,res,n); //凸包的大小(也就是凸包所包含点的数目) long long max = -1; for(int i = 0; i 
     
       data[i].y || (data[t].y == data[i].y) && data[t].x > data[i].x) t = i; } node temp = data[t]; data[t] = data[0]; data[0] = temp; //将除了data[0]之外的点按与data[0]的极角从小到大排序----------------------- /* 如果用注释的部分来排序会很慢,所以用快排 for(int i = 1; i < n; i++) { int t = i; for(int j =i+1; j < n; j++) { double flag = cross(data[t],data[j],data[0]); if(flag < 0 || ( flag == 0 && dis(data[t],data[0]) > dis(data[j], data[0]) ) ) t = j; } node temp1 = data[t]; data[t] = data[i]; data[i] = temp1; } */ sort(data + 1,data + n,cmp); //初始化栈res---------------------------------------------------------------- res[0] = data[0]; int top = 0; //利用栈不断维护凸壳---------------------------------------------------------- for(int i = 1; i < n; i++) { while(top > 0 && cross(res[top-1], data[i] ,res[top]) >= 0) //找出所有右转的点,然后弹出栈 top--; res[++top] = data[i]; } return top + 1; } double cross(node a, node b, node mark) { return (a.x - mark.x) * (b.y - mark.y) - (b.x - mark.x) * (a.y - mark.y); } double dis(node a, node b) { return sqrt( (a.x -b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) ); } 
      
     
    
  

 

 

 

 

 

 

 

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204628.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午7:54
下一篇 2026年3月19日 下午7:54


相关推荐

  • c++输入文件流ifstream用法详解

    c++输入文件流ifstream用法详解Andrew-&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;China:SaysHelloNoterightofChina:Chinathinks\naboutitChina–&amp;amp;amp;amp;amp;amp;amp;amp;amp;am

    2022年6月14日
    45
  • Pycharm配置opencv与numpy

    Pycharm配置opencv与numpy下载 opencv 与 numpy 的官网链接 https www lfd uci edu gohlke pythonlibs opencv 下载与 Python 版本和电脑操作系统版本相符合的 whl 文件 cp 后面的数字代表 python 版本 如 python3 8 的版本就是 cp38 python3 9 的版本就是 cp3 8 win 后面的就代表操作系统位数了 我的操作系统是 64 位 python 版本是 3 9 5 因此我下载的就是以下两个文件 下面介绍导入 opencv 的方法

    2026年3月27日
    2
  • Ubuntu修改用户名和密码后无法登录_ubuntu默认用户名

    Ubuntu修改用户名和密码后无法登录_ubuntu默认用户名ubuntu修改用户名和密码项目场景:克隆别人的虚拟机后,想修改用户名和密码。问题描述:修改密码后,root和用户身份验证正常,但是继续修改用户名后,提示认证失败。原因分析:原因是修改用户名后,用户名和密码不匹配解决方案:若要修改用户名和密码,需要切换到root后修改。修改完成后需要确认用户身份验证是否通过,通过后才证明完成了修改,否则重启后会导致无法登陆。下面是修改用户名和密码的步骤。1.修改密码最好先修改密码,重启后再修改用户名。1)修改root密码:$sudopasswdr

    2026年4月17日
    7
  • python读取txt文件中的数组

    python读取txt文件中的数组写此博客只是为做笔记defread_data(dir_str):”’此函数读取txt文件中的数据数据内容:科学计数法保存的多行两列数据输入:txt文件的路径输出:小数格式的数组,行列与txt文件中相同”’data_temp=[]withopen(dir_str)asfdata:wh

    2022年5月7日
    151
  • python字符串的比较

    python字符串的比较关于其中字符串类型id引用驻留机制见python中的字符串的驻留机制字符串的比较操作:运算符:>,>=,<,<=,==,!= 比较规则:首先比较两个字符串中的第–个字符,如果相等则继续比较下一个字符,依次比较下去,直到两个字符串中的字符不相等时,其比较结果就是两个字符串的比较结果,两个字符串中的所有后续字符将不再被比较比较原理:两字符进行比较时,比较的是其ordinalvalue(原始值),调用内置函数ord可以得到指定字符的ordinalv

    2022年6月18日
    38
  • MFC中的AssertValid和Dump函数

    MFC中的AssertValid和Dump函数本文转载自:VC调试中,AssertValid和Dump函数的应用——————————————————————————AssertValid()函数——————————————————————–

    2022年7月14日
    26

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号