凸包 初学 Andrew 和 Melkman (模板) 旋转卡(qia)壳(ke)
凸包初学
定义:
给你n个散落的点,让你求出最小的凸多边形将所有的点包括起来,或者点在边上。
稳定凸包,不能再扩充,每条边都用3个点
必备知识:
会用叉积判断点与直线的关系(这里指 点在线的那一边, 利用向量的叉积)
利用叉积比较点离线的距离
- 设 a 为直线所在向量 b为 直线末尾与点相连的向量,由叉积可得 a×b=|a||b|sin(<a,b>)a×b=|a||b|sin(<a,b>) a \times b =|a||b|sin() 我们可以利用a b向量的夹角来求 a×b>0a×b>0a\times b>0说明点在线的一边, a×b==0a×b==0a \times b ==0说明点在线上, a×b<0a×b<0a\times b说明点在线的另一边
- 还是 a×b=|a||b|sin(<a,b>)a×b=|a||b|sin(<a,b>) a \times b =|a||b|sin() , |b|sin(<a,b>)|b|sin(<a,b>) |b|sin() 就是点到直线的距离
叉积求法
二维
a=(x1,y1),b=(x2,y2)a=(x1,y1),b=(x2,y2)a=(x_{1},y_{1}) , b=(x_{2},y_{2})
a∗b=x1∗y2−x2∗y1a∗b=x1∗y2−x2∗y1a*b= x_{1}*y_{2}-x_{2}*y{1}
三维 略 (提示:行列式)
参考博客:
https://blog.csdn.net/u011001084/article/details/72768075
常用算法:
一、 暴力求解 (O(n3)O(n3)O(n^3))
- 暴力枚举每一条边,把所有的边都枚举出来
- 判段所有的点是不是在线的同一侧,是的话这条边就是,不是的话就不是
二、 Jarvis步进法 (O(nH)O(nH)O(nH))
- 先找一个 肯定是凸包上的点,例如 最低点 最左边的点
- 枚举所有点找与这个点和他前一个点组成夹角的最小点(初始只有一个点,则求与x轴的夹角)
三、 分冶法 (O(nlog(n))O(nlog(n))O(nlog(n)))
- 先求最左最右两点 把点分成两部分
- 分别求每部分到分线的距离找最远的,然后继续分
四、 Graham扫描法 (O(nlog(n))O(nlog(n))O(nlog(n)))
- 找一个最左边的点,以这个点为基准从新建立坐标系
- 把所有点按极角进行排序
- 按顺序扫描每个点,判断是否为左拐,是加入栈,不是弹出栈顶,直到满足然后入栈
缺点 : 精度问题 ,直接求会造成误差 ,可利用叉积排序 ,会造成丢点问题,详见 五
五、Andrew ( Graham扫描法 的改进版) (O(nlog(n))O(nlog(n))O(nlog(n)))
用 Graham扫描法 极角排序会造成丢点
例如
0 0
1 1
2 2
0 1
0 2
他扫描时会丢掉一个点,所以有人改进了极角排序,变成了以x y坐标排序
板子
struct paint{int x;int y;paint(){} paint(int xx,int yy):x(xx),y(yy){}bool friend operator <(const paint &a,const paint &b){if(a.x==b.x) return a.y<b.y;return a.x<b.x;}bool friend operator ==(const paint &a,const paint &b){return a.x==b.x&&a.y==b.y;}paint friend operator -(const paint &a,const paint &b){return paint(a.x-b.x,a.y-b.y);}
} ;
int cross(paint a,paint b){return a.x*b.y-b.x*a.y;
}
double dis(paint a,paint b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Andrew(vector<paint>p){int n=p.size();sort(p.begin(),p.end());vector<paint>t(n+1);int m=0;for(int i=0;i<n;i++){while(m>1&&cross(t[m-1]-t[m-2],p[i]-t[m-2])<=0) m--; //等号决定共线的点是否进入栈,也就是是否要凸边上的点t[m++]=p[i]; } int o=m;for(int i=n-2;i>=0;i--){while(m>o&&cross(t[m-1]-t[m-2],p[i]-t[m-2])<=0) m--;//等号决定共线的点是否进入栈,也就是是否要凸边上的点t[m++]=p[i];}if(n>1) m--;double ans=0;for(int i=1;i<m;i++){ans+=dis(t[i],t[i-1]);}return ans+dis(t[0],t[m-1]);
}
因为以x 和 y 排序所以每次只会求一半,需要正反扫两边 第二遍从n-2开始 因为第一遍的时候 n-1点肯定会进去
原理
还是利用夹角,让整个图形保持左转 (向左拐弯),先找一个最左边的点,因为经过排序,所以就是 0 号点了,先加入一个点(因为栈里只有一个点,无法构成角,直接跳过),在加一个点判断是否左拐(利用叉积 (角度)) 如果是就把点加入 ,如果不是就弹出栈顶,直到为左拐,把点加入,因为以x y排序第一遍找的都是y值大的点 也就是上凸包 下面没有,所以我们要反着找一遍 在第一遍时最后一个点肯定会加入所以第二遍就直接从倒数第二个点开始找了
入门题
- POJ 1113 Wall 凸包(模板题)
- POJ 1873 The Fortified Forest (进制压缩 + 模板) 附题解: 戳这里
六、 Melkman算法 (O(n)O(n)O(n))
这个是个O(n)O(n)O(n)的算法,感觉很牛掰,仔细看后有限制条件 有序
如果求多边形,多边形的点是有序的(就是定点按一定顺序编的号)可以直接用,否则必须排序
优点 : 在线算法,可以动态加点
板子
struct paint{int x;int y;paint(){} paint(int xx,int yy):x(xx),y(yy){}bool friend operator <(const paint &a,const paint &b){if(a.x==b.x) return a.y<b.y;return a.x<b.x;}bool friend operator ==(const paint &a,const paint &b){return a.x==b.x&&a.y==b.y;}paint friend operator -(const paint &a,const paint &b){return paint(a.x-b.x,a.y-b.y);}
} ;
int cross(paint a,paint b){return a.x*b.y-b.x*a.y;
}
double dis(paint a,paint b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Melkman(vector<paint>p){int n=p.size();vector<paint>q(n*2+10);int head,tail;head=tail=n;q[tail++]=p[0];int i;for(i=0;i<n-1;i++){q[tail]=p[i];if(cross(p[i]-q[head],p[i+1]-q[head])) break;}if(n==1) return 0;if(n==2) return dis(p[0],p[1]);if(n==3){return dis(p[0],p[1])+dis(p[0],p[2])+dis(p[1],p[2]);} q[--head]=q[++tail]=p[++i];if(cross(q[n+1]-q[n],q[n+2]-q[n])<0) swap(q[n],q[n+1]);for(++i;i<n;i++){if(cross(q[tail]-q[tail-1],p[i]-q[tail-1])>0&&cross(q[head]-q[head+1],p[i]-q[head+1])<0) continue; //凹凸全部满足,这个点是内部点,不考虑while(tail-head>1&&cross(q[tail]-q[tail-1],p[i]-q[tail-1])<=0) tail--;q[++tail]=p[i];while(tail-head>1&&cross(q[head]-q[head+1],p[i]-q[head+1])>=0) head++;q[--head]=p[i];}double ans=0;for(int i=head;i<tail;i++){ans+=dis(q[i],q[i+1]);}return ans;}
原理:
建一个小凸包,然后加入点 同时维护凹凸
利用双端队列进行维护,先找一个点扔到对首或队尾(一般是0号点) ,找连续两个点i, i+1
与 0
点能形成凸行的点,把i
点入队首,然后这时候队列里有两个点,再把i+1
同时入队首和队尾,这时候队里的点已经成了一个由三个点组成的小凸包,加入第四个点时开始判断,如果对首是凸(和四的左拐类似)就直接入队首,否则弹出队首,直到满足,入队首,如果队尾是凹行(右拐),就直接入队尾,否则弹出队尾,直到满足,入队尾
实际上就是在一个小的凸边行上逐渐加点,然后同时判断加的这个点在前面是否满足,和在后面是否满足
练习题目
- POJ 1113 Wall 凸包(模板题)
求点集中的点相距的最远距离( 旋转卡壳)
参考博客:
https://www.cnblogs.com/xdruid/archive/2012/07/01/2572303.html
思路:
- 相距最远的点肯定是凸包上的点,用 Andrew求出来凸包,然后暴力枚举凸包上所有点的距离
- 利用凸包旋转卡壳求
算法思路
- 卡壳,就是在凸包上做两个平行线,把凸包卡住,而卡住的点称为对踵点
- 然后旋转两条平行线,直到与一条边相重合,这时候你会发现,距离没在与平行线重合线上的点最远的点就是这个线上的两点中的一个
- 如果直接求,那么平行线很多,还是n2n2n^2的复杂度,这样的花还不如暴力求,但是你会发现一个规律,当你找到一个对应点,换边时(旋转),距离这条边最远的点在你之前找到那个点后面,也就是说没有必要再从头开始找,直接接着往后面找点
借用大佬的图
比如说你刚找完 上图颜色最多的那个
点,他的对应点就是最上面那个点,然后你旋转成左边相邻的那条边,这条边的最远点明显在刚才那个点的后面,也就是上面说的可以直接往后找,(具体的原理看上面的参考博客,大佬讲的很清楚)
模板
double RC(int len){ //len 指凸包点集合长度 vic[] 凸包点 cross 叉积 同上int p=1;;double ans=dist(vic[0],vic[1]);for(int i=0;i<len;i++){while(abs(cross(vic[(i+1)%len]-vic[i],vic[p]-vic[i]))<abs(cross(vic[(i+1)%len]-vic[i],vic[(p+1)%len]-vic[i])))p=(p+1)%len;ans=max(ans,max(dist(vic[p],vic[i]),dist(vic[(i+1)%len],vic[p])));}return ans;
}
练习题目
- POJ 2187 Beauty Contest 题解:
暂无
未完待续….
凸包 初学 Andrew 和 Melkman (模板) 旋转卡(qia)壳(ke)相关推荐
- poj2187 旋转卡(qia)壳(ke)
题意:求凸包的直径 关于对踵点对.旋转卡壳算法的介绍可以参考这里: http://www.cnblogs.com/Booble/archive/2011/04/03/2004865.html http ...
- 模板匹配 解决模板旋转以及重复检测问题
OpenCV Error: Assertion failed (scn == 3 || scn == 4)in cv::cvtColor, file C:\projects\opencv-python ...
- Css动画效果旋转卡牌
这次我们来实现一个用css动画效果敲打出来一个旋转卡牌的小案列. 首先把img图片框架敲打出来,给它一个div标签,里面包裹六个img标签. 放进去图片之后,然后给图片设置一下css样式. 设置tra ...
- poj 2187 凸包or旋转qia壳法
题意: 给n(50000)个点,求这些点与点之间距离最大的距离. 解析: 先求凸包然后暴力. 或者旋转卡壳大法. 代码: #include <iostream> #include < ...
- 旋转卡(qia)壳 平面最远点对
这个标题我以前一直读错... 旋转卡壳最基本应用便是求平面最远点对,被一对卡壳正好卡住的对应点对称为对踵点(Antipodal point),这里有一个结论,最远点对必然属于对踵点集. 引用别人的证明 ...
- python+opencv实现NCC模板旋转匹配(图像处理)
NCC匹配原理公式: 1.旋转的情况使用圆投影的旋转不变性原理匹配 2.通过对图像进行降采样,从降采样的高层开始匹配筛选匹配点极大地减少了运算量 3.差分简化运算数据量(但是目前实现的差分貌似没有起到 ...
- LeetCode 812. 最大三角形面积(再次用到凸包的Andrew算法) / 面试题 04.06. 后继者 / 953. 验证外星语词典
812. 最大三角形面积 2022.5.15 每日一题 题目描述 给定包含多个点的集合,从其中取三个点组成三角形,返回能组成的最大三角形的面积. 示例: 输入: points = [[0,0],[0, ...
- CAD制图初学入门:CAD布局旋转时要注意什么?
有些CAD制图初学入门者在使用浩辰CAD建筑软件绘制建筑图纸的过程中,有时候会需要用到CAD布局功能,那么在使用CAD布局旋转时有哪些注意事项呢?下面的CAD制图初学入门教程就让小编来给大家介绍一下在 ...
- BC 2015在百度之星程序设计大赛 - 预赛(1)(矩形区域-旋转卡)
矩形区域 Accepts: 717 Submissions: 1619 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 ...
最新文章
- AI 寻宝!美国女博士用 YOLOv3 打造沉船探测器,杰克船长:我错过了 100 亿
- [20160725]MyComparableTest
- 集大通100%任务完成度签到
- Kafka学习 之 理解Kafka集群(二)
- [注]什么是用户?估计90%人不知道
- python selenium firefox使用
- 为什么响应式编程并非一时之势?
- corosync +drbd实现HA MariaDB
- makefile函数集锦【转】
- 高洛峰细说php百度贴吧_高洛峰细说PHP视频教程推荐
- 计算机修改人类记忆曲线,Memory Helper
- JavaScript智能填写续写版(QQ邮箱地址栏简化版)
- 重拾英语-时态【转自鑫闻】
- 32java访问(http):国家气象局 免费接口
- 一个屌丝程序员的青春(四二)
- 在空间绘制出一系列螺旋上升的点
- 在互联网中,资源免费共享还能走多久!
- 354、什么是三网合一?三网合一如何建设
- 纯手工解密几大在线js加密网站(1)
- Sketch文件用什么软件打开