凸包及旋转卡壳求凸包直径

2024-01-10 07:18

本文主要是介绍凸包及旋转卡壳求凸包直径,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

那么,先提一下最基本最暴力的求凸包直径的方法吧---枚举。。。好吧。。很多问题都可以用 枚举 这个“万能”的方法来解决,过程很简单方便是肯定的,不过在效率上就要差很远了。 要求一个点集的直径,即使先计算出这个点集的凸包,然后再枚举凸包上的点对,这样来求点集直径的话依然会在凸包上点的数量达到O(n)级别是极大的降低它的效率,也浪费了凸包的优美性质。不过在数据量较小或者很适合时,何必要大费周折的用那些麻烦复杂的算法呢,枚举依然是解决问题的很好的方法之一。

然后就是今天的旋转卡壳算法了。(那个字念 qia 。。。搞了好久才发现读都读错了。囧。。。)

旋转卡壳可以用于求凸包的直径、宽度,两个不相交凸包间的最大距离和最小距离等。虽然算法的思想不难理解,但是实现起来真的很容易让人“卡壳”。

其实简单来说就是用一对平行线“卡”住凸包进行旋转。

被一对卡壳正好卡住的对应点对称为对踵点,对锺点的具体定义不好说,不过从图上还是比较好理解的。

可以证明对踵点的个数不超过3N/2个 也就是说对踵点的个数是O(N)

对踵点的个数也是我们下面解决问题时间复杂度的保证。

卡壳呢,具体来说有两种情况:

1.

一种是这样,两个平行线正好卡着两个点;

2.

一种是这样,分别卡着一条边和一个点。

而第二种情况在实现中比较容易处理,这里就只研究第二种情况。

在第二种情况中 我们可以看到 一个对踵点和对应边之间的距离比其他点要大(借用某大神的图··)

也就是一个对踵点和对应边所形成的三角形是最大的 下面我们会据此得到对踵点的简化求法。

下面给出一个官方的说明:

Compute the polygon's extreme points in the y direction. Call them ymin and ymax. Construct two horizontal lines of support through ymin and ymax. Since this is already an anti-podal pair, compute the distance, and keep as maximum. Rotate the lines until one is flush with an edge of the polygon. A new anti-podal pair is determined. Compute the new distance, compare to old maximum, and update if necessary. Repeat steps 3 and 4 until the anti-podal pair considered is (ymin,ymax) again. Output the pair(s) determining the maximum as the diameter pair(s).

要是真的按这个实现起来就麻烦到吐了。。

根据上面的第二种情况,我们可以得到下面的方法:

如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。

直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。

然而我们可以发现 凸包上的点依次与对应边产生的距离成单峰函数(如下图:)

这个性质就很重要啦。

根据这个凸包的特性,我们注意到逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算。于是我们得到了O(n)的算法。这就是所谓的“旋转”吧!

利用旋转卡壳,我们可以在O(n)的时间内得到凸包的对锺点中的长度最长的点对。

又由于最远点对必然属于对踵点对集合 ,那么我们先求出凸包 然后求出对踵点对集合 然后选出距离最大的即可

http://poj.org/problem?id=2187

AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring>
#define N 50005
using namespace std;
struct Point
{
int x;
int y;
}p[N];
int top,n,s[N];//s中存的是凸包包含的极点
int dis(const Point& a,const Point& b)
{return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);}
int cross(const Point& a,const Point& b,const Point& c)
{return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}
bool cmp(const Point& a,const Point& b)//幅角排序
{
int ans=cross(p[0],a,b);
if((ans>0)||(ans==0&&dis(p[0],a)<dis(p[0],b))) return true;//再求凸包时可以删除共线的那个点
return false;
}
void graham()//凸包模板
{
s[0]=0;
s[1]=1;
top=1;
for(int i=2;i!=n;++i)
{
while(top&&cross(p[s[top-1]],p[s[top]],p[i])<0) top--;
s[++top]=i;
}
top++;
}
void doit()//旋转卡壳
{
int q=1,ans=dis(p[s[0]],p[s[1]]);
for(int i=0;i!=top;++i)
{
while(abs(cross(p[s[(i+1)%top]],p[s[i%top]],p[s[(q+1)%top]]))>abs(cross(p[s[(i+1)%top]],p[s[i%top]],p[s[q%top]])))  q=(q+1)%top;
ans=max(ans,max(dis(p[s[(i+1)%top]],p[s[q]]),dis(p[s[i%top]],p[s[q]])));
}
printf("%d\n",ans);
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i!=n;++i)
scanf("%d%d",&p[i].x,&p[i].y);
Point ans=p[0];
int u=0;
for(int i=1;i!=n;++i)
if((ans.y>p[i].y)||(ans.y==p[i].y&&ans.x>p[i].x)) ans=p[i],u=i;
if(u) swap(p[u],p[0]);
sort(p+1,p+n,cmp);
memset(s,0,sizeof(s));
graham();
doit();	   
}return 0;
}


 

这篇关于凸包及旋转卡壳求凸包直径的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/589991

相关文章

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

poj 2187 凸包or旋转qia壳法

题意: 给n(50000)个点,求这些点与点之间距离最大的距离。 解析: 先求凸包然后暴力。 或者旋转卡壳大法。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <s

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

二维旋转公式

二维旋转公式 ros的tf工具包可以很方便的实现任意坐标系之间的坐标转换。但是,如果只是想简单的测试想法,而又不想编写过于庞杂的代码,考虑自己写二维旋转的函数。而与二维旋转问题对偶的另一个问题便是二维坐标系旋转变换。这两个问题的形式基本一样,只是旋转的角度相差一个负号。就是这个容易搞混,所以做个笔记,以备查用。 1. 二维旋转公式(算法) 而(此文只针对二维)旋转则是表示某一坐标点 ( x

算法复杂度 —— 数据结构前言、算法效率、时间复杂度、空间复杂度、常见复杂度对比、复杂度算法题(旋转数组)

目录 一、数据结构前言 1、数据结构 2、算法 3、学习方法 二、 算法效率 引入概念:算法复杂度  三、时间复杂度 1、大O的渐进表示法 2、时间复杂度计算示例  四、空间复杂度 计算示例:空间复杂度 五、常见复杂度对比 六、复杂度算法题(旋转数组) 1、思路1 2、思路2 3、思路3 一、数据结构前言 1、数据结构         数据结构(D

计算几何之向量旋转

实际做题中我们可能会遇到很多有关及计算几何的问题,其中有一类问题就是向量的旋转问题,下面我们来具体探讨一下有关旋转的问题。 首先我们先把问题简化一下,我们先研究一个点绕另一个点旋转一定角度的问题。已知A点坐标(x1,y1),B点坐标(x2,y2),我们需要求得A点绕着B点旋转θ度后的位置。 A点绕B点旋转θ角度后得到的点,问题是我们要如何才能得到A' 点的坐标。(向逆时针方向旋转角度正,

红黑树的旋转

红黑树的基本性质 红黑树与普通的二叉搜索树不同,它在每个节点上附加了一个额外的属性——颜色,该颜色可以是红色或黑色。通过引入这些颜色,红黑树能够维持以下 5 个基本性质,以确保树的平衡性: 每个节点是红色或黑色。根节点是黑色。所有叶子节点(NIL 节点)是黑色。如果一个节点是红色的,那么它的两个子节点都是黑色的(即,红色节点不能有红色子节点)。从任一节点到其每个叶子节点的所有路径上都包含相同数

yolov8-obb旋转目标检测onnxruntime和tensorrt推理

onnxruntime推理 导出onnx模型: from ultralytics import YOLOmodel = YOLO("yolov8n-obb.pt") model.export(format="onnx") onnx模型结构如下: python推理代码: import cv2import mathimport numpy as npimport onnxr

AVL 树的旋转

什么是 AVL 树? AVL 树是一种自平衡二叉搜索树(Binary Search Tree, BST),以其发明者 G. M. Adelson-Velsky 和 E. M. Landis 的名字命名。它的特点是对于任意一个节点,其左右子树的高度差(平衡因子)不超过 1,这确保了 AVL 树的高度在 O(log n) 的范围内,从而保证了插入、删除和查找操作的时间复杂度都是 O(log n)。