这一世,要学会二维凸包

玩玩二维凸包

前言&注明

最近在学计几(计算几何),然后……
在这里插入图片描述
精神状态越来越好了。(阿辉~)

本文只写了二维凸包的一种解法:扫描法。个人认为和 FHQ_Treap 一样,都是解同类问题的算法中易懂且好写。

网上的一些解析有点抽象(对于我),所以打算写一篇易懂(?)的讲解(给未来的自己)。

参考文献:

  • 极角(From:百度百科);
  • 几何:极角排序详解;
  • 数论小白都能看懂的平面凸包详解(From:洛谷,@ShineEternal);
  • 凸包(From:OI-Wiki);
  • 凸包算法详解(From:CSDN,@努力攻坚操作系统)。

参考文献有点多,这说明了什么?

正式启动之前,先去看看向量和角的弧度制表示法。

在这里插入图片描述

极角,启动!

BlastMike:你充710试试?

注意:极角一定要学,这东西没学就别想着学扫描法了。

极角

定义:在极坐标系中,平面上任何一点到极点的连线和极轴的夹角叫做极角。

光看定义十分的抽象,不如结合图来了解。
请添加图片描述

平面上取一定点 O O O,从 O O O 引一条水平射线 O x O_x Ox,规定方向自左至右,再选定一个长度单位并规定角旋转的正方向,通常取逆时针方向,这样就构成了一个极坐标系,如图所示,点 O O O 叫作极点,射线 O x O_x Ox 叫作极轴。

在极坐标系中,平面上任意一点 M M M 的位置,可以由 O M OM OM 的长度 ρ \rho ρ 和从到 O M OM OM 的角 θ \theta θ 来确定,把 ρ \rho ρ 叫作点 M M M 的极径, θ \theta θ 叫作点M的极角,有序实数对 ( ρ , θ ) (\rho,\theta) (ρ,θ) 叫作点M的极坐标,记作 ( ρ , θ ) (\rho,\theta) (ρ,θ)

特别地,当点 M M M 在极点时,它的坐标是 ( 0 , θ ) (0,\theta) (0,θ) θ \theta θ 可以取任意值,当点M在极轴上时,它的坐标是 ( ρ , 0 ) (\rho,0) (ρ,0) ρ \rho ρ 可以取任意正值。

如图2所示,在极坐标系中,各点的极坐标分别为: ( 2 , π 3 ) , ( 3 , 3 π 4 ) , ( 1 , π 6 ) , ( 2 , 11 π 6 ) (2,\frac{\pi}{3}),(3,\frac{3 \pi}{4}),(1,\frac{\pi}{6}),(2,\frac{11 \pi}{6}) (2,3π),(3,43π),(1,6π),(2,611π)
请添加图片描述

就学这么多就够了。

极角排序

这里讲利用叉积按极角从小到大排序。

设向量 a → _a^\rarr a b → _b^\rarr b,的叉积为 x x x

对于 x x x

  • x = 0 x=0 x=0:是指两向量平行(或者说两向量重合);
  • x > 0 x>0 x>0:则 a → _a^\rarr a 在向量 b → _b^\rarr b 的顺时针方向(可理解为在 a → _a^\rarr a b → _b^\rarr b 的下方);
  • x < 0 x<0 x<0:则 a → _a^\rarr a b → _b^\rarr b 的逆时针方向(可理解为在 a → _a^\rarr a b → _b^\rarr b 的上方)。

学扫描法只需要这么多,其他可以自己拓展。

扫描法(Graham算法)开始

先说明这个算法的时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)

扫描法的第一步是对点集进行排序,这样能保证其有序性,从而在后续的处理中达到更高效的效果,所以扫描法比其他普通算法更优的原因。

例题:P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包 。

详细解释

(图片全 ∑ \sum 洛谷的,懒得画)

假设共 m m m 个点。

我们选择一个 y y y 值最小的点,如有相同选 x x x 最小的点,记为 P 1 P_1 P1

剩下的点集中按照极角的大小逆时针排序,然后编号为 P 2 ∼ P m P_2 \sim P_m P2Pm

草

BlastMike:这是在种草?
在这里插入图片描述

我们按照排序结束后的顺序枚举每一个点,依次连线,可以使用一个栈来存储,每次入栈,如果即将入栈的元素与栈顶两个元素所构成了一个类似于凹壳(凹!)的东西,那么显然处于顶点的那个点一定不在这个点集的凸包上,而他正好在栈顶,所以把它弹出栈,新点入栈。

但是,新来的点有可能既踢走了栈顶,再连接新的栈顶元素后却发现仍然可以踢出,故此时就不能忘记判断。(不然会趋势)

如果你不想纠缠于繁杂的文字描述(简单地说就是语言认知能力差),那么下面就有动图解说献上。(早该说了)

在这里插入图片描述
至此,扫描法解释完成。

由于我们在排序的帮助下省去了一些盲目的扫描,虽然排序作为一个预处理时间复杂度占据了总时间复杂度,但相比 Andrew 算法还是更为优秀。

结合代码

检查叉积

计算叉积,检查叉积是否大于 0 0 0,如果是 a a a 就逆时针转到 b b b

代码片段:

inline double Cross(Point a, Point b, Point c) {return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
计算两点之间距离

这就不用解释了吧。

代码片段:

inline double Get_Distance(Point a, Point b) {return sqrt((a.x - b.x) * (a.x - b.x) * 1.0 + (a.y - b.y) * (a.y - b.y) * 1.0);
}
重要的排序函数

注意:这个函数别写错了,要不然功亏一篑。

判断叉积是否大于 0 0 0,若是,则一定满足要求(上面有说)。

特别的,若叉积等于 0 0 0,则说明两向量在同一直线上,要判断他们到 p 0 p_0 p0(初始点)的距离。

代码片段:

inline bool Compar_2(Point a, Point b) {double m = Cross(Stack[0], a, b);if (m > 0)return 1;else if (m == 0 && Get_Distance(Stack[0], a) - Get_Distance(Stack[0], b) <= 0)return 1;else return 0;
}

Code

BlastMike:没有 Code 的 BestMonkey 的博客是没有灵魂的。

#include <bits/stdc++.h>
using namespace std;
struct Point {double x, y;
} P[100005], Stack[100005];
int n, Top = 1;
double Answer;
inline double Cross(Point a, Point b, Point c) {return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}
inline double Get_Distance(Point a, Point b) {return sqrt((a.x - b.x) * (a.x - b.x) * 1.0 + (a.y - b.y) * (a.y - b.y) * 1.0);
}
inline bool Compar_1(Point a, Point b) {if (a.y == b.y)return a.x < b.x;return a.y < b.y;
}
inline bool Compar_2(Point a, Point b) {double m = Cross(Stack[0], a, b);if (m > 0)return 1;else if (m == 0 && Get_Distance(Stack[0], a) - Get_Distance(Stack[0], b) <= 0)return 1;else return 0;
}
signed main() {scanf("%d", &n);for (register int i = 0; i < n; ++i)scanf("%lf%lf", &P[i].x, &P[i].y);if (n == 1) puts("0.00"), exit(0);else if (n == 2) printf("%.2lf\n", Get_Distance(P[0], P[1])), exit(0);else {memset(Stack, 0, sizeof(Stack)), sort(P, P + n, Compar_1), Stack[0] = P[0], sort(P + 1, P + n, Compar_2), Stack[1] = P[1];//最低点一定在凸包里for (register int i = 2; i < n; ++i) {while (Cross(Stack[Top - 1], Stack[Top], P[i]) < 0)//判断前面点的会不会被踢走,如果被踢走那么出栈Top--;Stack[++Top] = P[i];}for (register int i = 0; i < Top; ++i)Answer += Get_Distance(Stack[i], Stack[i + 1]);Answer += Get_Distance(Stack[0], Stack[Top]), printf("%.2lf\n", Answer), exit(0);}return 0;
}

后记

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2979006.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA 2021.3.3最新激活破解教程(可激活至2099年,亲测有效)

1、ja-netfilter-all Windows 系统&#xff0c;点击运行 install-current-user.vbs 脚本&#xff0c;为当前用户安装破解补丁 截图是window环境下的激活方式 运行此补丁大约花费几分钟&#xff0c;点击 确定&#xff0c; 等待 Done 完成提示框出现&#xff0c;到这里&#xf…

YashanDB V23.2 LTS发版 | 共享集群首个长期支持版本

4月&#xff0c;YashanDB正式发布长期支持版本YashanDB V23.2 LTS&#xff0c;标志着YashanDB单机主备、共享集群和分布式实时数仓等完整产品体系&#xff0c;已全面进入可规模化使用的长期支持阶段&#xff1b;同时配套数据迁移工具、监控运维工具和开发者工具&#xff0c;可以…

S32 Design Studio PE工具配置canCom

工具配置 基本就是默认配置就行&#xff0c;有需要的话就按照下面的方式改改。 生成代码 在Generated_Code/canCom1.c里面&#xff0c;对应刚才配置的信息。canCom1_InitConfig0是配置结构体&#xff0c;canCom1_State是初始化之后的状态结构体。 flexcan_state_t canCom1_S…

docker 虚拟化与docker的概念

一、云计算的三种服务模式 laas、pass、saas 1.1 IaaS: Infrastructure-as-a-Service&#xff08;基础设施即服务&#xff09; 第一层叫做IaaS&#xff0c;有时候也叫做Hardware-as-a-Service&#xff0c;几年前如果你想在办公室或者公司的网站上运行一些企业应用&#xff0c…

06:HAL----定时器

前言&#xff1a; 每来一个TIM 时钟CNT计数器就记一个数&#xff0c;记到某一个程度就会产生溢出。然后ARR就会装载到CNT计数器里面 一:TIM 1:介绍 TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计…

牛客周赛 Round 40(A,B,C,D,E,F)

比赛链接 官方讲解 这场简单&#xff0c;没考什么算法&#xff0c;感觉有点水。D是个分组01背包&#xff0c;01背包的一点小拓展&#xff0c;没写过的可以看看&#xff0c;这个分类以及这个题目本身都是很板的。E感觉就是排名放高了导致没人敢写&#xff0c;本质上是个找规律…

消费增值:革新你的消费体验,让每一分钱都更有价值

亲爱的顾客们&#xff0c;你们好&#xff01;今天&#xff0c;我想为大家介绍一种革新性的消费模式——消费增值&#xff0c;它赋予每一次购物以额外的价值&#xff0c;让消费过程变得更加丰富多彩。 过去&#xff0c;我们的消费观念往往是“一手交钱&#xff0c;一手交货”&am…

Pytorch:张量的梯度计算

目录 一、自动微分简单介绍1、基本原理2、梯度计算过程3、示例&#xff1a;基于 PyTorch 的自动微分a.示例详解b.梯度计算过程c.可视化计算图 4、总结 二、为什么要计算损失&#xff0c;为何权重更新是对的&#xff1f;1、梯度下降数学原理2、梯度上升 三、在模型中使用自动微分…

Hbuilder快捷键个人习惯修改

自定义修改 [// {"key":"ctrld","command":"editor.action.deleteLines"},// {"key":"ctrle","command":"editor.action.addSelectionToNextFindMatch"}//目录内查找字符串{"key"…

DC-DC电源设计中电感选型详解

电感参数: DC-DC 电感选型步骤: 1, 根据 DC-DC 的输入输出特性计算所需的最小电感量。 (1)对于 Buck 型 DC-DC,计算公式如下 Lmin= 【Vout*(1-Vout/Vinmax)】/ (Fsw*Irpp ) 其中: Vinmax = maximum input voltage Vout = output voltage fsw = switching frequency…

电路仿真,为何国产软件成首选?

随着科技的飞速发展&#xff0c;电路仿真技术在电子工程设计中的作用日益凸显。面对市场上琳琅满目的电路仿真软件&#xff0c;为何我们应该优先选择国产软件呢&#xff1f;本文将从多个方面为您深入解析。 一、国产软件的安全性保障 在当前国际形势下&#xff0c;信息安全尤为…

[2024更新]如何从Android恢复已删除的相机照片?

相信大家都经历过Android手机误删相机图片的经历。您是否正在寻找一种可行的方法来挽救这些丢失的照片&#xff1f;如果这是你迫切想解决的问题&#xff0c;那么这篇文章绝对可以帮助你。然而&#xff0c;与其考虑如何从Android恢复已删除的相机照片&#xff0c;我们更愿意建议…

【C++类和对象】日期类的实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

万兆以太网MAC设计(6)IP协议报文格式详解以及IP层模块设计

文章目录 前言&#xff1a;IPv4报文协议格式二、IP_RX模块设计2.1、模块接口2.2、模块工作过程 三、IP_TX模块设计3.1、模块接口3.2、模块工作过程 四、仿真4.1、发送端4.2、接受端 前言&#xff1a;IPv4报文协议格式 参考&#xff1a;https://sunyunqiang.com/blog/ipv4_prot…

【Linux学习】初始冯诺漫体系结构

文章目录 认识冯诺依曼系统 认识冯诺依曼系统 什么是冯诺依曼体系结构&#xff1f; 冯诺依曼体系结构是一种将程序指令和数据以二进制形式存放在主存储器中&#xff0c;由中央处理器统一控制和执行的计算机系统结构。冯诺依曼体系结构实现了程序的可编程性和硬件与软件的分离&…

jdk版本冲突,java.lang.UnsupportedClassVersionError: JVMCFRE003

主要是编辑器所用的jdk版本和项目用的不一致导致的&#xff0c;虽然编译通过了&#xff0c;但是运行是会报错 选好后点击Apply点击ok&#xff0c;然后重新编译一遍项目就可以了

OpenTelemetry-1.介绍

目录 1.是什么 2.为什么使用 OpenTelemetry 3.数据类型 Tracing Metrics Logging Baggage 4.架构图 5.核心概念 6.相关开源项目 ​编辑 7.分布式追踪的起源 8.百花齐放的分布式追踪 Zipkin Skywalking Pinpoint Jaeger OpenCensus OpenTracing 9.Openteleme…

Linux运维之道:深入探索开源世界的基石

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

【LeetCode热题100】【多维动态规划】最小路径和

题目链接&#xff1a;64. 最小路径和 - 力扣&#xff08;LeetCode&#xff09; 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 经典动态规…

向量的点积和叉积的几何意义

1. 点积 点积(dot product)&#xff0c;又称标量积&#xff08;scalar product&#xff09;。结果等于。 可用于 判断的是否垂直求投影长度求向量是抑制作用还是促进作用计算两个向量的夹角 2. 叉积 叉积(cross product)&#xff0c;又称为向量积(vector product)。模长等…