当前位置: 首页 > news >正文

凸包进阶旋转卡壳(模板题目集)

前置了解:

 Andrew算法(求凸包)-CSDN博客

旋转卡壳

(Rotating Calipers)算法是一种用于解决凸包相关问题的高效算法,尤其适用于计算凸包直径(即凸包上任意两点间的最大距离)、最小包围矩形等问题 

旋转卡壳算法的核心思想是:通过一对平行线(卡壳)“夹住” 凸包,并旋转这对平行线,在旋转过程中枚举所有可能的对踵点对(即凸包上距离最远的点对),从而找到凸包的直径。 

1. 对踵点对(Antipodal Pairs)
  • 定义:如果过凸包上的两个点可以作一对平行切线,使得凸包夹在这两条平行线之间,则称这两个点为一对对踵点。
  • 性质:凸包的直径一定是某一对对踵点之间的距离。因此,只需要枚举所有对踵点对,计算它们之间的距离,取最大值即可。
2. 旋转过程
  • 边 - 点对:对于凸包上的每条边,找到距离该边最远的点。这个点和边的两个端点构成潜在的对踵点对。
  • 旋转对称性:当一条边对应的最远点确定后,随着边按顺序旋转,对应的最远点也会按顺序移动(不会回溯),因此可以在线性时间内枚举所有对踵点对。

 

 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;

struct vec{
    double x,y;
    bool operator<(const vec& other) const {
        if(x != other.x) return x < other.x;
        return y < other.y;
    }
};//凸包:比较函数

vec p[N], st[N];

double cross(vec A, vec B){
    return A.x*B.y-A.y*B.x; 
}//凸包,叉乘函数

double Side(vec a, vec b, vec p)  {
    vec A={b.x-a.x, b.y-a.y}; 
    vec B={p.x-a.x, p.y-a.y}; 
    return cross(A,B);
}//凸包,根据3点两向量叉乘求左右

double dis2(vec a, vec b) {
    return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}//答案,平方距离

int Andrew(int n) {
    if(n == 0) return 0;
    
    sort(p+1, p+n+1);
    
    int top = 0;
    
    // 构建下凸
    for(int i=1; i<=n; i++){
        while(top >= 2 && Side(st[top-2], st[top-1], p[i]) <= 0)
            top--;
        st[top++] = p[i];
    }
    
    int temp = top;
 
    // 构建上凸
    for(int i=n-1; i>=1; i--){
        while(top > temp && Side(st[top-2], st[top-1], p[i]) <= 0)
            top--;
        st[top++] = p[i];
    }
    
    // 如果只有一个点,凸包就是这个点本身
    if(n == 1) return 1;
    
    // 如果有两个点,凸包就是这两个点
    if(n == 2) return 2;
    
    // 移除重复的起点
    if(top > 1) top--;
    
    return top;
}

double rotating_calipers(int n){/核心转圈圈
    if(n < 2) return 0;
    if(n == 2) return dis2(st[0], st[1]);//12特判
    
       double res = 0;
    int j = 1;  // 初始化最远点为 st[1]
    
            // 枚举每条边 st[i] - st[i+1]
    for(int i=0; i<n; i++){
        // 找到边 st[i]-st[i+1] 对应的最远点 st[j]
        while(Side(st[i], st[(i+1)%n], st[j]) < Side(st[i], st[(i+1)%n], st[(j+1)%n]))//n只是为了防首尾那一个点
            j = (j+1)%n;
        
        // 更新最大距离
        res = max(res, max(dis2(st[i], st[j]), dis2(st[(i+1)%n], st[j])));
        
        // 检查对踵点对 (i, j+1) 和 (i+1, j+1)
        res = max(res, max(dis2(st[i], st[(j+1)%n]), dis2(st[(i+1)%n], st[(j+1)%n])));/直接线性了!!
    }
    
    return res;
}

int main(){
    int n;
    cin >> n;
    
    for(int i=1; i<=n; i++){
        cin >> p[i].x >> p[i].y;
    }
    
    int m = Andrew(n);
    
    cout << fixed << setprecision(0) << rotating_calipers(m) << endl;
    
    return 0;
}

题目2

 很明显,对踵点对他又要发挥作用惹

以下代码只有黑色部分与上面不同

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;

struct vec{
    double x,y;
    bool operator<(const vec& other) const {
        if(x != other.x) return x < other.x;
        return y < other.y;
    }
};

vec p[N], st[N];

double cross(vec A, vec B){
    return A.x*B.y-A.y*B.x; 
}

double Side(vec a, vec b, vec p)  {
    vec A={b.x-a.x, b.y-a.y}; 
    vec B={p.x-a.x, p.y-a.y}; 
    return cross(A,B);
}

double dis2(vec a, vec b) {
    return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}//没用,只不过没有删

int Andrew(int n) {
    if(n == 0) return 0;
        sort(p+1, p+n+1);  
    int top = 0;
    for(int i=1; i<=n; i++){
        while(top >= 2 && Side(st[top-2], st[top-1], p[i]) <= 0)
            top--;
        st[top++] = p[i];
    }
    int temp = top;
    for(int i=n-1; i>=1; i--){
        while(top > temp && Side(st[top-2], st[top-1], p[i]) <= 0)
            top--;
        st[top++] = p[i];
    }   
    if(n == 1) return 1;   
    if(n == 2) return 2;    
    if(top > 1) top--;   
    return top;
}

double rotating_calipers(int n){
    double ans=0;
    for (int i=1;i<n;i++){
        int a=i,b=i+1;

        for (int j=i+1;j<=n;j++){

//众所周知,叉乘是两向量的对应平行四边形的面积。。。。。。


            while (Side(st[i],st[j],st[a+1])<Side(st[i],st[j],st[a])) a=a%n+1;//这里的上面为什么是小于号:原因:在该算法中,我们其实要遍历每条对角线,也就是i-j线,假设j现在离i比较远,由于a初始是i,很明显加1之后i-a在i-j的右边,叉积是负数。。。。。所以<才对;;而b为什么又需要大于呢?根据已有的j=i+1;很明显从一开始,b+1的点一定是要大于b点(0),所以从一开始,b就被推到了i-j的上面,也就是左侧,,,很明显成为了正数;然后对于一条对角线,很明显我们需要在他的左右侧各找一个最大三角形来围最后的最大四边形。。完美!


            while (Side(st[i],st[j],st[b+1])>Side(st[i],st[j],st[b])) b=b%n+1;//主要还是加1,%n还是为了防止那个首尾连接点。。作用:通过不断迭代,找到选择了当前边i=1,j=i+1的最大面积;之后因为ab已经++,不会回溯,就是开始逆时针转圈圈了(i大循环是会回溯的)
            ans=max(ans,-Side(st[i],st[j],st[a])+Side(st[i],st[j],st[b]));
        }
    }
    return ans/2;
}

int main(){
    int n;
    cin >> n;
    
    for(int i=1; i<=n; i++){
        cin >> p[i].x >> p[i].y;
    }
    
    int m = Andrew(n);
    
    cout << fixed << setprecision(3) << rotating_calipers(m) << endl;
    
    return 0;
}

http://www.dtcms.com/a/263783.html

相关文章:

  • Window 2000 Perfectional_配置和管理FTP
  • uniapp内置蓝牙打印
  • Qt小组件 - 1(手风琴)
  • 计算机网络:【socket】【UDP】【地址转换函数】【TCP】
  • 测试第六讲-测试模型分类
  • CloudBase AI ToolKit实战:从0到1开发一个智能医疗网站
  • 时空数据挖掘五大革新方向详解篇!
  • 电机控制——电机位置传感器零位标定
  • 安防监控视频汇聚平台EasyCVR v3.7.2版云端录像无法在web端播放的原因排查和解决方法
  • springboot3.2/3.4+rocketmq5.3.3测试程序的基本例子
  • SSVEP Next:现代化的 SSVEP 可视化 Web 快速实现
  • CAU数据挖掘实验 表分析数据插件
  • 【第二章:机器学习与神经网络概述】04.回归算法理论与实践 -(2)支持向量回归(SVR)
  • 软考中级【网络工程师】第6版教材 第1章 计算机网络概述
  • MATLAB构建capm和三因子模型
  • (论文总结)语言模型中的多模态思维链推理
  • 记一次Ubuntu22安装MongoDB8并同步本地数据过程
  • 动手学深度学习13.5. 多尺度目标检测-笔记练习(PyTorch)
  • FPGA FMC 接口
  • 【仿muduo库实现并发服务器】LoopThreadPool模块
  • Linux安装JDK和Maven
  • 多探头分布式雷达测流系统解决方案概述
  • 洛谷P1379 八数码难题【A-star】
  • 目标检测在国防和政府的应用实例
  • vue-i18n+vscode+vue 多语言使用
  • 缺乏对新成员的有效融入机制,如何改进
  • 学习昇腾开发的第12天--安装第三方依赖
  • 【Linux基础知识系列】第三十八篇 - 打印系统与 PDF 工具
  • Ubuntu 20.04 下 OpenVINO 2024 安装与配置
  • 业界优秀的零信任安全管理系统产品介绍