【计算几何】凸包问题 (Convex Hull)

news2025/1/10 23:24:30

【计算几何】凸包问题 (Convex Hull)

引言

凸多边形

凸多边形是指所有内角大小都在 [ 0 , π ] [0,π] [0,π]范围内的简单多边形

凸包

在平面上能包含所有给定点的最小凸多边形叫做凸包。

其定义为:对于给定集合 X,所有包含 X 的凸集的交集 S 被称为 X 的 凸包。

实际上可以理解为用一个橡皮筋包含住所有给定点的形态。

凸包用最小的周长围住了给定的所有点。如果一个凹多边形围住了所有的点,它的周长一定不是最小,如下图。根据三角不等式,凸多边形在周长上一定是最优的。

在这里插入图片描述

凸包的求法

主要介绍Graham扫描法

Graham Scan 算法求凸包

Graham Scan 算法是一种十分简单高效的二维凸包算法,能够在 O ( n l o g n ) O(nlogn) O(nlogn)
的时间内找到凸包。

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

在这里插入图片描述
在这里插入图片描述

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

然后插入第三个点 C,并计算 A B ⃗ × B C ⃗ \vec{AB} \times \vec{BC} AB ×BC 的向量积,却发现向量积系数小于(等于)0,也就是说 B C ⃗ \vec{BC} BC
A B ⃗ \vec{AB} AB 的顺时针方向上。

在这里插入图片描述

于是删去 B 点。

在这里插入图片描述

按照这样的方法依次扫描,找完 “下壳” 后,再找 “上壳”。


#include <algorithm>

struct Point {
    double x, y;

    // 重载减法运算符,用于计算向量差
    Point operator-(Point& p) {
        Point t;
        t.x = x - p.x;
        t.y = y - p.y;
        return t;
    }

    // 计算向量叉积
    double cross(Point p) {
        return x * p.y - p.x * y;
    }
};

// 比较函数,用于排序点
bool cmp(Point& p1, Point& p2) {
    if (p1.x != p2.x) return p1.x < p2.x;
    return p1.y < p2.y;
}

Point point[1005];  // 无序点
int convex[1005];   // 保存组成凸包的点的下标
int n;              // 坐标系的无序点的个数

// 获取凸包的函数
int GetConvexHull() {
    // 按照x坐标排序,如果x相同则按照y坐标排序
    sort(point, point + n, cmp);
    int temp;
    int total = 0;

    // 构建下凸包
    for (int i = 0; i < n; i++) {
        // 如果当前栈中至少有两个点,并且新点使得栈顶的两个点与新点形成的向量不满足逆时针方向
        while (total > 1 &&
               (point[convex[total - 1]] - point[convex[total - 2]])
                       .cross(point[i] - point[convex[total - 1]]) <= 0)
            total--;  // 弹出栈顶点

        convex[total++] = i;  // 将当前点加入栈中
    }

    temp = total;  // 记录下凸包的点数

    // 构建上凸包
    for (int i = n - 2; i >= 0; i--) {
        // 如果当前栈中至少有两个点,并且新点使得栈顶的两个点与新点形成的向量不满足逆时针方向
        while (total > temp &&
               (point[convex[total - 1]] - point[convex[total - 2]])
                       .cross(point[i] - point[convex[total - 1]]) <= 0)
            total--;  // 弹出栈顶点

        convex[total++] = i;  // 将当前点加入栈中
    }

    return total - 1;  // 返回组成凸包的点的个数,实际上多了一个,就是起点,所以组成凸包的点个数是 total - 1
}

代码解释

结构体 Point:

  • 包含两个成员变量 x 和 y,表示点的坐标。

  • 重载了减法运算符 operator-,用于计算两个点的向量差。

  • 定义了 cross 方法,用于计算两个向量的叉积。

比较函数 cmp:

  • 用于对点进行排序,首先按 x 坐标排序,如果 x 坐标相同则按 y 坐标排序。

全局变量:

  • point 数组存储无序点。

  • convex 数组存储组成凸包的点的下标。

  • n 表示无序点的个数。

函数 G e t C o n v e x H u l l GetConvexHull GetConvexHull:

  • 首先对点进行排序。

  • 构建下凸包:从左到右遍历点,使用栈来维护凸包的点,确保每次新加入的点与栈顶的两个点形成的向量满足逆时针方向。

  • 构建上凸包:从右到左遍历点,使用栈来维护凸包的点,确保每次新加入的点与栈顶的两个点形成的向量满足逆时针方向。

  • 返回组成凸包的点的个数,注意实际点的个数是 total - 1,因为起点被重复计算了一次。

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

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

相关文章

苍穹外卖--编辑员工和根据id查询员工信息

查询员工信息 编辑员工 package com.sky.controller.admin;import com.sky.constant.JwtClaimsConstant; import com.sky.dto.EmployeeDTO; import com.sky.dto.EmployeeLoginDTO; import com.sky.dto.EmployeePageQueryDTO; import com.sky.entity.Employee; import com.sky…

oracle索引字段存储数据过长,导致索引失效

1&#xff1a;短位数据&#xff0c;索引生效 2&#xff1a;长位索引&#xff0c;索引不生效 此问题发现于6月中旬&#xff0c;线上问题优化。引以为戒。 解决&#xff1a; 并未解决索引不生效问题&#xff0c; 但是基于优化查询&#xff0c;是的查询保持毫秒级

21.呼吸灯拓展练习,控制四颗led以不同的频率“呼吸”

&#xff08;1&#xff09;Verilog代码&#xff1a; module ex_breath_led(clk,reset_n,led_out);input clk;input reset_n;output [3:0]led_out;reg [5:0]cnt_1us;reg [9:0]cnt_1ms;reg [10:0]cnt_2ms;reg [11:0]cnt_4ms;reg [12:0]cnt_8ms;reg [9:0]cnt_1s;reg overturn;par…

2024 Q3 NAND闪存价格|企业级依然猛涨,消费级放缓

在企业领域持续投资于服务器基础设施&#xff0c;特别是在人工智能应用的推动下&#xff0c;企业级SSD需求增加的同时&#xff0c;消费电子市场却依旧疲软。加之NAND供应商在2024年下半年积极扩大生产&#xff0c;预计到2024年第三季度&#xff0c;NAND闪存供应充足率将上升至2…

【CPP】CPP的命名空间输入输出缺省参数函数重载

目录 1 命名空间 -- namespace2 CPP的输入与输出(io)2.1 输入输出流的一些规定2.2 实操一下2.3 关于endl2.4 关于精度控制2.5 效率提高 3 缺省参数(默认参数)3.1 样例3.2 全缺省与半缺省3.3 缺省参数的意义 4 函数重载4.1 函数重载的基本使用4.2 函数重载调用歧义 这里是oldkin…

产品原型设计:从概念到实现的完整指南

如果你是一位产品经理&#xff0c;那么你一定会和原型图打交道&#xff0c;产品原型是产品设计方案和底层逻辑的可视化表达&#xff0c;需要完整清晰地表达出产品目的及需求&#xff0c;在整个产品创造的过程中发挥着不可或缺的作用。而对于一些刚入行的产品经理来说&#xff0…

Docker学习笔记(三)Dockerfile

一、什么是Dockerfile Dockerfile 是一个用于自动化构建 Docker 镜像的文本文件&#xff0c;其中包含了从一个基础镜像开始&#xff0c;到最终形成所需定制镜像的所有指令集。这个文件中的每一条指令都对应着构建镜像过程中的一个步骤或一层&#xff0c;指导 Docker 如何安装软…

tk 文本生成器

import random import tkinter as tk import ttkbootstrap as ttk from tkinter import messagebox import pyperclipdef wenben_run():def generate_text(original_text, length):# 去掉原始文本中的换行符和空格original_text original_text.replace(\n, )original_text or…

【文档智能】LACE:帮你自动生成文档布局的方法浅尝

前言 往期很多文章都介绍了【文档智能】上布局识别&#xff08;版式分析&#xff09;的技术思路&#xff0c;版式分析是通过对文档版式进行布局识别&#xff0c;识别文档中的元素类型的过程。这次来看看一个有趣的思路&#xff0c;通过已有的元素类型&#xff0c;来生成可控的…

解决安卓tv 蓝牙遥控器配对后输入法弹不出来的问题

t972在蓝牙配对后,自带的LatinIME 输入法会出现弹不出来的现象。 经过分析,主要为蓝牙的kl 文件适配存在问题。解决如下: 1.新建 kl文件Vendor_2b54_Product_1600.kl 放到 /vendor/usr/keylayout/下 内容: #for bl remote add by jason 20240709 key 113 VOLUME_MUTE …

【RAG KG】GraphRAG开源:查询聚焦摘要的图RAG方法

前言 传统的 RAG 方法在处理针对整个文本语料库的全局性问题时存在不足&#xff0c;例如查询&#xff1a;“数据中的前 5 个主题是什么&#xff1f;” 对于此类问题&#xff0c;是因为这类问题本质上是查询聚焦的摘要&#xff08;Query-Focused Summarization, QFS&#xff09…

FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884 (Mac OS X)

FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884 (Mac OS X) 主页多媒体音频编辑FL Studio 24.1.1.4234 (Windows) / 24.1.1.3884... FL Studio 图标 FL Studio&#xff08;前身为 FruityLoops&#xff09;是一款功能强大的音乐制作环境或数字音频工作站&#xff08;DAW&#x…

用于视频生成的扩散模型

学习自https://lilianweng.github.io/posts/2024-04-12-diffusion-video/ 文章目录 3D UNet和DiTVDMImagen VideoSora 调整图像模型生成视频Make-A-Video&#xff08;对视频数据微调&#xff09;Tune-A-VideoGen-1视频 LDMSVD稳定视频扩散 免训练Text2Video-ZeroControlVideo 参…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥协商(ArkTS)】

密钥协商(ArkTS) 以协商密钥类型为X25519 256&#xff0c;并密钥仅在HUKS内使用为例&#xff0c;完成密钥协商。 开发步骤 生成密钥 设备A、设备B各自生成一个非对称密钥&#xff0c;具体请参考[密钥生成]或[密钥导入]。 密钥生成时&#xff0c;可指定参数HUKS_TAG_DERIVE…

STL--栈(stack)

stack 栈是一种只在一端(栈顶)进行数据插入(入栈)和删除(出栈)的数据结构,它满足后进先出(LIFO)的特性。 使用push(入栈)将数据放入stack,使用pop(出栈)将元素从容器中移除。 使用stack,必须包含头文件: #include<stack>在头文件中,class stack定义如下: namespace std…

前端面试题32(浅谈前端热部署)

前端热部署&#xff08;Hot Deployment&#xff09;是指在开发过程中&#xff0c;当开发者修改了前端代码后&#xff0c;这些修改能够立即反映到正在运行的前端应用上&#xff0c;而无需重新启动整个应用或服务器。这种能力极大地提高了开发效率&#xff0c;因为开发者可以即时…

算法学习笔记(8.2)-动态规划入门进阶

目录 问题判断: 问题求解步骤&#xff1a; 图例&#xff1a; 解析&#xff1a; 方法一&#xff1a;暴力搜索 实现代码如下所示&#xff1a; 解析&#xff1a; 方法二&#xff1a;记忆化搜索 代码示例&#xff1a; 解析&#xff1a; 方法三&#xff1a;动态规划 空间…

如何在JetBrains中写Codeforce?

目录 前言 正文 leetcode 个人喜好 参考资料 具体操作步骤 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ningbo China&#x1f4…

硬件:CPU和GPU

一、CPU与GPU 二、提升CPU利用率&#xff1a;计组学过的 1、超线程一般是给不一样的任务的计算使用&#xff0c;而非在计算密集型工作中 2、Cpu一次可以计算一个线程&#xff0c;而gpu有多少个绿点一次就能计算多少个线程&#xff0c;Gpu比cpu快是因为gpu它的核多&#xff0c;…

如何在 PostgreSQL 中确保数据的异地备份安全性?

文章目录 一、备份策略1. 全量备份与增量备份相结合2. 定义合理的备份周期3. 选择合适的备份时间 二、加密备份数据1. 使用 PostgreSQL 的内置加密功能2. 使用第三方加密工具 三、安全的传输方式1. SSH 隧道2. SFTP3. VPN 连接 四、异地存储的安全性1. 云存储服务2. 内部存储设…