问题介绍

给定平面上一些点的集合,找到一些点,使得这些点形成一个凸的包围,围住所有的点,如图

思路

采用分治法,将点集合一分为二,整体的凸包问题可以分为【求上半部分的凸包】+【求下半部分的凸包】

分策略

将集合一分为二的策略是:将点按照x升序排序,x相同则按y升序,然后选取0和最后一个下标,以这两点做一条直线,这两点一定是最左边和最右边的,我们用 pa 和 pb 表示这两点

为子问题求解划分范围

已经将集合一分为二了,那么子问题的求解区间该怎么划定呢?

遍历所有的点

  • 找到在【直线pa pb】上方,距离【直线pa pb】最远的点 pmax
  • 找到在【直线pa pb】下方,距离【直线pa pb】最远的点 pmin

最远距离表示

这个距离我们可以用他们三点组成的三角形的面积来表示,这个行列式可以求解,值得注意的是,p1一定是在p2左边,p1,p2组成直线,p3是我们要判断的那个点

  • 最后得出的值大于0,说明 p3 在【直线 p1 p2】上方
  • 最后得出的值小于0,说明 p3 在【直线 p1 p2】下方
  • 最后得出的值等于0,说明 p3 在【直线 p1 p2】上

    我们找到在【直线pa pb】上方,距离【直线pa pb】最远的点 pmax,找到在【直线pa pb】下方,距离【直线pa pb】最远的点 pmin,将 pa, pb, pmax, pmin 连起来,得到如下的图

    把 【直线 pa pmax 】上方的点作为下一次查找的集合 s1
    把 【直线 pmax pb 】上方的点作为下一次查找的集合 s2
    把 【直线 pa pmin 】下方的点作为下一次查找的集合 s3
    把 【直线 pmin pb 】下方的点作为下一次查找的集合 s4

分别递归四个区域点的集合,值得注意的是

使用points数组存储点
使用vis数组,表示下标为 i 的点是不是凸包上的点,vis[i] = 1则是

  • 递归的边界情况,点集合的数目小于3,说明所有点都在凸包上,vis 置 1
  • 在直线上的点,也要加入下一次的点的集合
  • 如果递归的是上半部分的集合,那么之后的所有递归都只用针对上半部分,递归 s1 s2
  • 如果递归的是下半部分的集合,那么之后所有的递归都只用针对下半部分,递归 s3 s4
  • 如果递归的是全体集合(只有第一次递归会发送这个情况),需要同时递归上下部分,即同时递归 s1 s2 s3 s4

代码

输入:

12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2

输出

(1, 1)
(1, 2)
(2, 0)
(2, 3)
(4, 0)
(5, 1)
(5, 4)
(6, 2)
#include <bits/stdc++.h>using namespace std;// 结构定义
typedef struct p
{int x, y;
}p;bool cmp(const p& p1, const p& p2)
{if(p1.x==p2.x) return p1.y<p2.y;return p1.x<p2.x;
}// 存放点
p points[100];
int vis[100];// 计算p1,p2,pi三个点组成的三角形面积
int peak(p p1, p p2, p pi)
{return p1.x*p2.y + pi.x*p1.y + p2.x*pi.y - pi.x*p2.y - p2.x*p1.y - p1.x*pi.y;
}// 递归求凸包
// ps 是当前要求解的点的集合,ps保存这些点在points数组中的下标
// mode 表示递归s1 s2,还是递归s3,s4,还是同时递归 s1,s2,s3,s4
// mode = 3 递归 s1,s2,s3,s4,只有第一次调用会出现该情况
// mode = 2 递归s3,s4
// mode = 1 递归s1 s2
void hull(vector<int> &ps, int mode)
{// 边界处理:少于两个点的集合一定是凸包上的点 if(ps.size()<=2){for(int i=0; i<ps.size(); i++) vis[ps[i]]=1;return;}// 最左右一定是凸包上的点, pa最左点,pb最右点 vis[ps.front()]=1, vis[ps.back()]=1;p pa=points[ps.front()], pb=points[ps.back()];// 找距离 pa,pb组成的直线最远的点,imax是上方最远,imin是下方最远 int maxs=INT_MIN, mins=INT_MAX, imax=-1, imin=-1;for(int i=1; i<ps.size()-1; i++){int s = peak(pa, pb, points[ps[i]]);if(s>maxs && s>=0) maxs=s, imax=ps[i];if(s<mins && s<=0) mins=s, imin=ps[i];}// pa,pb与imax,imin的连线,分割出下一趟递归的点集合 s1 s2 s3 s4vector<int> s1, s2, s3, s4;for(int i=0; i<ps.size()-1; i++){if(peak(pa, points[imax], points[ps[i]])>=0) s1.push_back(ps[i]);if(peak(pa, points[imin], points[ps[i]])<=0) s3.push_back(ps[i]);}for(int i=1; i<ps.size(); i++){if(peak(points[imax], pb, points[ps[i]])>=0) s2.push_back(ps[i]);if(peak(points[imin], pb, points[ps[i]])<=0) s4.push_back(ps[i]);}if(mode==3)hull(s1, 1), hull(s2, 1), hull(s3, 2), hull(s4, 2);else if(mode==1) hull(s1, 1), hull(s2, 1);else if(mode==2) hull(s3, 2), hull(s4, 2);
} int main()
{int n, x, y;cin>>n;vector<int> ps(n);for(int i=0; i<n; i++)cin>>points[i].x>>points[i].y, ps[i]=i;sort(points, points+n, cmp);hull(ps, 3);for(int i=0; i<n; i++)if(vis[i]==1) cout<<"("<<points[i].x<<", "<<points[i].y<<")"<<endl;return 0;
}/*
12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2
*/

凸包问题 分治法求解相关推荐

  1. 快包_分治法求解凸包问题

    凸包问题(分治法) 题目简述 P2742 [USACO5.1]圈奶牛Fencing the Cows   农夫约翰想要建造一个围栏用来围住他的奶牛,可是他资金匮乏.他建造的围栏必须包括他的奶牛喜欢吃草 ...

  2. 分治法求解集合的众数及其重数

    1. 分治法 分治法解题过程主要分为分.治.合三个步骤",应用该方法的基本过程如下: (1) 将原问题分解为若干个规模较小的子问题 (2) 对这些子问题分别求解 (3) 对各个子问题的解进行 ...

  3. 算法设计与分析 实验二 分治法求解最近点对问题

    分治法求解最近点对问题 一.实验目的与要求 1.实验基本要求 2.实验亮点 二.实验内容与方法 三.实验步骤与过程 (一)一些准备工作 1.实验流程 2.数据生成与去除重复点 (二)暴力穷举法 1.算 ...

  4. 分治法 分治法求解递推式

    分治法 分治法基本就是下面的三步 分(divide):无法有效解决的划分更小的问题 治(conquer):递归求每一个子问题的解 合(combine):合并解得出原问题解 MergeSort:排列 1 ...

  5. 分治法求解大整数乘法

    算法导论课作业:分治法求解大整数乘法 – 学号:20204227058 求解思想 ​ 实现大整数乘法的方法有许多种,其中我们最简单的方法就是小学里面教的竖式算法,这种方法在计算过程中数AAA需要和数B ...

  6. java 循环赛问题,网球循环赛思路 - 分治法求解(无代码)

    分治法: 列出人数为的情况: K = 1 1 2 2 1 其中第一列是选手的序号,之后n列代表着选手的对手 K = 2 1 2 3 4 2 1 4 3 3 4 1 2 4 3 2 1 可以看出,k=4 ...

  7. 输油管道问题-分治法求解

    题目要求:某石油公司计划建造一条由东向西的主输油管道.该管道要穿过一个有n口油井的油田.从每口油井都要有一条输油管道沿最短路经(或南或北)与主管道相连. 如果给定n口油井的位置,即它们的x坐标(东西向 ...

  8. 分治法求解最大子数组问题

    最大子数组问题求解 将数组A分成两部分,A[left...mid]和A[mid+1..right]两部分,求解最大子数组之和包含了三种可能的情况: 1.完全位于子数组A[left...mid]中,因此 ...

  9. 算法设计棋盘覆盖问题c语言,棋盘覆盖问题(用分治法求解)

    // 棋盘覆盖 #include #include int Board[8][8]={0};//定义棋盘并初始化棋盘 void ChessBoard(int tr,int tc,int dr,int ...

最新文章

  1. 帧间编码的预测自适应量化系数扫描排序
  2. 添加白名单_上网行为管理如何添加网站白名单(包括https网站)
  3. ES6新特性之函数优化-箭头函数
  4. [未来的购碟指南]送给那些以后想收藏真人电影DVD的菜鸟们
  5. 设计模式:单例模式7种写法
  6. python大小写转换_python字符串大小写转换
  7. 商务部回应中兴被罚:坚决反对美国用国内法制裁中企
  8. 微信小程序数据懒加载
  9. 空间数据格式转换之MapInfo mid/mif文件转ArcGIS shpfile文件
  10. 15日精读掌握《高德纳:具体数学》计划(2019.5/27-2019/6/10)
  11. 微信公众号H5 - 使用vue开发微信公众号网页
  12. 前端面试-面试官常见问题
  13. 行测中数量关系的练习题集(07-29)【2】
  14. vs工程生成自定义图标的exe
  15. Java添加一个滑动验证码,有啥可难的,分分钟加一个
  16. Windows下用Python获取电脑显示器物理尺寸和PPI
  17. 如何为自己制作出一份优秀的简历
  18. linux服务器上的回收站,为linux系统实现回收站
  19. 如何成为用户真正需要的短信验证平台
  20. Excel设置格式后必须双击才生效问题的解决办法

热门文章

  1. 解决 vue中props对象中设置多个默认值问题
  2. 在打包过程中小米手机的apk图标失败的解决方法
  3. C#中Split的使用
  4. 计算机组成原理2代表,计算机组成原理2.ppt
  5. 使用Python轻松识别音频中文字(Whisper)
  6. 味尚食品|味尚拉面一款不容错过的半干面
  7. CVPR2020-超强通道注意力模块ECANet!
  8. OS学习笔记1——分段与分页
  9. 如何在小红书营销中发布图文笔记,打造优质营销内容技巧
  10. 勇往直前的“盛斗士“之比格云倾情放送6折全系云主机