首页 > 编程语言 >【算法模板】计算几何:旋转卡壳求凸包直径

【算法模板】计算几何:旋转卡壳求凸包直径

时间:2024-08-14 21:25:03浏览次数:18  
标签:std const Point hull 凸包 卡壳 求凸包 模板

旋转卡壳算法是一种几何算法,主要用于在二维平面上求解与凸包相关的最优问题。该算法利用凸包顶点的顺序性和对称性,通过模拟两个卡壳(calipers)沿着凸包边界的旋转来寻找最优解。常见的应用包括计算凸包的直径(即最远点对之间的距离)、最小包围矩形(最小面积矩形),以及最小宽度(宽度最小的方向)。

算法思想

旋转卡壳算法的核心思想是通过模拟两个相互平行的“卡壳”在凸多边形的边缘上旋转,来找到特定的几何关系或最优解。该算法利用了凸多边形的凸性性质,使得在计算过程中可以高效地从一个顶点移动到下一个顶点,从而避免了暴力搜索中不必要的计算。

算法流程

以计算凸多边形的直径为例,下面是旋转卡壳算法的基本步骤:

  1. 构造凸包
    • 首先,给定一个点集,使用算法(如 Graham 扫描或 Andrew 算法)计算出该点集的凸包。凸包是最小的凸多边形,能够包含所有的点。
  2. 初始化卡壳
    • 选择凸包中的任意一个点作为起点,设定两个相互平行的卡壳(线段),其中一个卡壳与凸包的某一条边重合。通常选择一条凸包的边作为初始卡壳。
  3. 旋转卡壳
    • 开始模拟卡壳的旋转过程。具体来说,对于每个顶点,考虑卡壳与凸包边的夹角(即旋转的角度)。
    • 将其中一个卡壳固定在当前边上,旋转另一个卡壳,使得它始终与凸包的一条边保持平行。旋转过程中,保持卡壳之间的距离最大化。
  4. 更新最优解
    • 在旋转过程中,每次更新卡壳时,计算卡壳之间的距离(即计算当前点对的距离)。记录并更新最大距离,直到卡壳旋转回到起始位置。
  5. 终止条件
    • 当卡壳旋转一周,回到初始位置时,算法终止。此时记录的最大距离即为凸多边形的直径。

算法模板

前置模板

using Real = double;  // 定义浮点数类型为 Real
using Point = std::pair<Real, Real>; // 使用 std::pair 来表示二维平面中的一个点

// 计算向量 AB 和向量 AC 的叉积
Real cross(const Point& A, const Point& B, const Point& C) {
    auto [Ax, Ay] = A; auto [Bx, By] = B; auto [Cx, Cy] = C;
    return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax);
}

// 计算向量 AB 和向量 AC 的点积
Real dot(const Point& A, const Point& B, const Point& C) {
    auto [Ax, Ay] = A; auto [Bx, By] = B; auto [Cx, Cy] = C;
    return (Bx - Ax) * (Cx - Ax) + (By - Ay) * (Cy - Ay);
}

// 计算两点之间的距离平方
Real distanceSquared(const Point& A, const Point& B) {
    return dot(A,B,B);
}

// 计算两点之间的欧几里得距离
Real distance(const Point& A, const Point& B) {
    return std::sqrt(distanceSquared(A, B));
}

// 凸包算法(Andrew 算法实现)
std::vector<Point> convexHull(std::vector<Point>& points) {
    std::sort(points.begin(), points.end());
    std::vector<Point> hull;
    for (const Point& p : points) {
        while (hull.size() >= 2 && cross(hull[hull.size() - 2], hull.back(), p) <= 0)
            hull.pop_back();
        hull.push_back(p);
    }
    size_t t = hull.size() + 1;
    for (int i = points.size() - 1; i >= 0; --i) {
        while (hull.size() >= t && cross(hull[hull.size() - 2], hull.back(), points[i]) <= 0)
            hull.pop_back();
        hull.push_back(points[i]);
    }
    hull.pop_back();
    return hull;
}

凸包直径

// 旋转卡壳算法求凸包的直径(即最远点对的距离)
Real rotatingCalipers(const std::vector<Point>& hull) {
    int n = hull.size(); // 获取凸包点的数量

    // 如果凸包只有一个点,直径为0
    if (n == 1) return 0;

    Real maxDist = 0; // 初始化最大距离的平方为0
    int j = 1; // j 是与 i 相对的点的索引

    // 遍历凸包上的每个点 i
    for (int i = 0; i < n; ++i) {
        // 通过旋转卡壳,找到使距离最大化的点 j
        while (cross(hull[i], hull[(i + 1) % n], hull[(j + 1) % n]) >= 
               cross(hull[i], hull[(i + 1) % n], hull[j])) {
            // 如果 j 的下一个点与 i 的距离更大,则更新 j
            j = (j + 1) % n;
        }
        // 更新最大距离的平方
        maxDist = std::max(maxDist, distanceSquared(hull[i], hull[j]));
        maxDist = std::max(maxDist, distanceSquared(hull[(i + 1) % n], hull[j]));
    }

    return sqrtl(maxDist); // 返回凸包直径
}

例题

H-Two Convex Polygons_2024牛客暑期多校训练营9 (nowcoder.com)

在二维平面上,给定两个凸多边形 AB(可能有三个共线点)。其中,A 是固定的。你可以在平面上平移、旋转或翻转 B,但要确保 AB 至少有一个点重合。求 B 可能覆盖的图形的周长。

#include <bits/stdc++.h>
using namespace std;

using Real = long double;
using Point = std::pair<Real, Real>; 
Real cross(const Point &A, const Point &B, const Point &C);
Real distanceSquared(const Point &A, const Point &B);
Real rotatingCalipers(const std::vector<Point> &hull);

signed main(){
    int task;
    cin >> task;
    while (task--){
        int n;
        cin >> n;
        vector<Point> vpa(n);
        for (auto &[x, y] : vpa){
            int _x,_y;
            cin>>_x>>_y;
            x=_x,y=_y;
        }
        int m;
        cin >> m;
        vector<Point> vpb(m);
        for (auto &[x, y] : vpb){
            int _x,_y;
            cin>>_x>>_y;
            x=_x,y=_y;
        }

        long double ans = 0;
        for (int i = 1; i <= n; i++)
            ans += sqrtl(distanceSquared(vpa[i - 1], vpa[(i) % n]));
        long double R = rotatingCalipers(vpb);
        ans += R * M_PI * 2;
        cout << ans << endl;
    }
    return 0;
}

标签:std,const,Point,hull,凸包,卡壳,求凸包,模板
From: https://blog.csdn.net/2303_80346267/article/details/141200584

相关文章

  • antd模板工程
    pnpmcreatevite@latestmy-project----templatereactcdmy-projectpnpminstall-Dtailwindcsspostcssautoprefixernpxtailwindcssinit-ptailwind.config.js:/**@type{import('tailwindcss').Config}*/exportdefault{corePlugins:{......
  • ABP默认模板修改默认数据库类型并初始化数据库数据
    我这里以SQLite数据库为例,其他数据库类似。1.下载模板https://aspnetboilerplate.com/ 根据自己的需求选择版本和前端框架并填写项目名称,点击“Createmyproject!”即可下载一个ABP标准模板项目。  解压下载好的压缩包,找到目录:aspnet-core,接下来就可以用VS打开.sln......
  • c++常用模板(持续更新中)
    二分手写#include<bits/stdc++.h>usingnamespacestd;intn,m;inta[N];boolf=0;intFIND(intx){ intl=1,r=n; while(l<=r) { intmid=(l+r)/2; if(x==a[mid])returnmid; if(x<a[mid])r=mid-1; if(x>a[mid])l=mid+1; } return-1;......
  • 回溯算法介绍以及模板
    回溯算法的理解:回溯算法可以理解为一颗树形结构,即一颗n叉树,当遍历到叶子节点的时候,我们就到达了递归的终点,此时我们应该往上走。回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!因为回溯法解决的都是在集合中递归查找子集,集合的大小就......
  • linux内核模块 字符设备驱动模板
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、linux内核模块是什么?二、代码示例总结前言提示:这里可以添加本文要记录的大概内容:内核版本5.10.92linux内核模块字符设备驱动模板cdev注册字符设备,创建一个/dev/下设备节点和/sy......
  • P5431 【模板】模意义下的乘法逆元 2
    看到5e6的数据,500ms的时限,\(O(NlogN)\)快速幂直接跑肯定会T掉,那我们就要考虑优化一下式子。我们令\(s=\prod_{1}^{n}{a[i]}\),那我们给第i个式子通分,就为$\frac{k^i*s/a[i]}{s}$\(s/a[i]\)就相当于$\prod^{i-1}_{1}{a[i]}*\prod_{i+1}^{n}{a[i]}$因此我们只需要预......
  • pbootcms新手必读|安装需知|环境要求|快速部署|获取授权码|模板制作
    环境要求服务器:Linux/Windows/Nginx/Apache/IIS PHP版本:不小于5.4,完美支持php7。推荐PHP5.6和PHP7.3MYSQL版本:5.0以上。推荐使用5.5+快速部署1、将官网下载的压缩包里面所有文件和文件夹上传到你的网站根目录(支持安装在二级目录)2、数据库默认采用的是sqlite,不......
  • [YM]模板-单链表(超详细简洁模板)
    概念:链表是一种用于存储数据的数据结构,通过如链条一般的指针来连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳,复杂度几乎是O(n)但其还是有很大的重要性是数据结构的开端模板:题目概述:相信大家对于单链表的操作已经游刃有余了,我们知道,对于一个单......
  • pbootcms模板自动清理runtime缓存
    打开/apps/home/controller/ExtLabelController.php文件找到  //测试扩展单个标签  privatefunctiontest()  {    $this->content=str_replace('{pboot:userip}',get_user_ip(),$this->content);  }}在它下面加入//自动会话清理脚本publicfunc......
  • Makefile 编译多级目录多个目标文件模板
    对于当前目录结构下的Makefile(基于图书管理系统).├──Makefile├──README.md├──bin│├──adminsys│└──usersys├──build│├──adminmain.o│├──generalcore.o│├──generalimpl.o│├──generalview.o│├──......