赞
踩
旋转卡壳算法是一种几何算法,主要用于在二维平面上求解与凸包相关的最优问题。该算法利用凸包顶点的顺序性和对称性,通过模拟两个卡壳(calipers)沿着凸包边界的旋转来寻找最优解。常见的应用包括计算凸包的直径(即最远点对之间的距离)、最小包围矩形(最小面积矩形),以及最小宽度(宽度最小的方向)。
旋转卡壳算法的核心思想是通过模拟两个相互平行的“卡壳”在凸多边形的边缘上旋转,来找到特定的几何关系或最优解。该算法利用了凸多边形的凸性性质,使得在计算过程中可以高效地从一个顶点移动到下一个顶点,从而避免了暴力搜索中不必要的计算。
以计算凸多边形的直径为例,下面是旋转卡壳算法的基本步骤:
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)
在二维平面上,给定两个凸多边形 A 和 B(可能有三个共线点)。其中,A 是固定的。你可以在平面上平移、旋转或翻转 B,但要确保 A 和 B 至少有一个点重合。求 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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。