假设集合 S S S是平面上 n > 1 n>1 n>1个点 p 1 ( x 1 , y 1 ) , . . . , p n ( x n , y n ) p_1(x_1,y_1),...,p_n(x_n,y_n) p1​(x1​,y1​),...,pn​(xn​,yn​)构成的。我们还假设这些点是按照它们的 x x x轴坐标升序排列的,如果 x x x轴坐标相同,则按照 y y y轴坐标升序排列。
算法步骤:

  1. 排序后找到最左边的点 p 1 p_1 p1​和最右边的点 p 2 p_2 p2​(因为这两个点经过证明是凸包的顶点)。

  2. 利用向量 p 1 p n → \overrightarrow{p_1p_n} p1​pn​ ​将点分成上包 S 1 S_1 S1​和下包 S 2 S_2 S2​,对于点 p r p_r pr​( p r p_r pr​不能是 p 1 p n → \overrightarrow{p_1p_n} p1​pn​ ​上的点,因为 p 1 p n → \overrightarrow{p_1p_n} p1​pn​ ​上的点不可能再为凸包的顶点):

    • 如果 p 1 p n → × p 1 p r → > 0 \overrightarrow{p_1p_n}\times\overrightarrow{p_1p_r}>0 p1​pn​ ​×p1​pr​ ​>0,则 p 1 p r → \overrightarrow{p_1p_r} p1​pr​ ​在 p 1 p n → \overrightarrow{p_1p_n} p1​pn​ ​的左侧,即 p r p_r pr​是上包 S 1 S_1 S1​内的点。
    • 如果 p 1 p n → × p 1 p r → < 0 \overrightarrow{p_1p_n}\times\overrightarrow{p_1p_r}<0 p1​pn​ ​×p1​pr​ ​<0,则 p 1 p r → \overrightarrow{p_1p_r} p1​pr​ ​在 p 1 p n → \overrightarrow{p_1p_n} p1​pn​ ​的右侧,即 p r p_r pr​是下包 S 2 S_2 S2​内的点。
      注:这里进行的是向量的叉积运算,设二维向量 a ⃗ = ( a 1 , a 2 ) \vec{a}=(a_1,a_2) a =(a1​,a2​), b ⃗ = ( b 1 , b 2 ) \vec{b}=(b_1,b_2) b =(b1​,b2​),那么 a × b = a 1 b 2 − a 2 b 1 a\times b=a_1b_2-a_2b_1 a×b=a1​b2​−a2​b1​(具体内容见高数的解析几何部分)
  3. 将点分类到上包和下包后,分别在其中找出顶点 p m a x p_{max} pmax​,它是使三角形 △ p 1 p m a x p n \triangle p_1p_{max}p_n △p1​pmax​pn​面积最大的点。如果 q 1 ( x 1 , y 1 ) , q 2 ( x 2 , y 2 ) , q 3 ( x 3 , y 3 ) q_1(x_1,y_1),q_2(x_2,y_2),q_3(x_3,y_3) q1​(x1​,y1​),q2​(x2​,y2​),q3​(x3​,y3​)是平面上的任意三个点,那么三角形 △ q 1 q 2 q 3 \triangle q_1q_2q_3 △q1​q2​q3​的面积等于下面这个行列式绝对值的二分之一
    ∣ x 1 y 1 1 x 2 y 2 1 x 3 y 3 1 ∣ = x 1 y 2 + x 3 y 1 + x 2 y 3 − x 3 y 2 − x 2 y 1 − x 1 y 3 \left| \begin{array}{cccc} x_1 & y_1 & 1 \\ x_2 & y_2 & 1 \\ x_3 & y_3 & 1 \end{array} \right|=x_1y_2+x_3y_1+x_2y_3-x_3y_2-x_2y_1-x_1y_3 ∣∣∣∣∣∣​x1​x2​x3​​y1​y2​y3​​111​∣∣∣∣∣∣​=x1​y2​+x3​y1​+x2​y3​−x3​y2​−x2​y1​−x1​y3​

  4. 找到 p m a x p_{max} pmax​之后,这里以上包举例,可以证明

    • p m a x p_{max} pmax​是上包的顶点
    • 包含在 △ p 1 p m a x p n \triangle p_1p_{max}p_n △p1​pmax​pn​之中的点不可能是上包的顶点(因此在后面不必考虑)。
    • 同时位于 p 1 p m a x → \overrightarrow{p_1p_{max}} p1​pmax​ ​和 p m a x p n → \overrightarrow{p_{max}p_n} pmax​pn​ ​的左边的点是不存在的
  5. 然后利用向量 p 1 p m a x → \overrightarrow{p_1p_{max}} p1​pmax​ ​找到位于 p 1 p m a x → \overrightarrow{p_1p_{max}} p1​pmax​ ​左侧的点,然后返回第3步的做法找到顶点;利用向量 p m a x p n → \overrightarrow{p_{max}p_n} pmax​pn​ ​找到位于 p m a x p n → \overrightarrow{p_{max}p_n} pmax​pn​ ​右侧的点,然后返回第3步的做法找到顶点,依次类推,最后找出上包的所有顶点,下包的做法也类似。

实现代码(C++)

#include<vector>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define Up true
#define Down false
// 输出包围pts的凸包void helper(vector<pair<int,int>> pts,pair<int,int>A,pair<int,int>B,bool up,vector<pair<int,int>>& res) {if (pts.size()==0) return;vector<pair<int,int>> LeftPts;vector<pair<int,int>> RightPts;pair<int,int> AB = make_pair(B.first-A.first, B.second-A.second);int maxArea = 0;pair<int, int> Pmax;bool flag = false;if(up){// 找到使得APmaxB面积最大的点for(auto p:pts) {int Area = abs(A.first*p.second + B.first*A.second + p.first*B.second - B.first*p.second - p.first*A.second - A.first*B.second)/2; if(Area > maxArea) {Pmax = p;maxArea = Area;flag = true;}}pair<int,int> APmax = make_pair(Pmax.first-A.first, Pmax.second-A.second);pair<int,int> PmaxB = make_pair(B.first-Pmax.first, B.second-Pmax.second);for(auto p:pts) {pair<int,int> AP = make_pair(p.first-A.first, p.second-A.second);pair<int,int> PmaxP = make_pair(p.first-Pmax.first, p.second-Pmax.second);// 找出APmax左边的点if((APmax.first * AP.second - AP.first * APmax.second) > 0) {LeftPts.push_back(p);   }// 找出PmaxB右边的点else if(PmaxB.first * PmaxP.second - PmaxP.first * PmaxB.second > 0) {RightPts.push_back(p);}}}else {// 找到使得APmaxB面积最大的点for(auto p:pts) {int Area = abs(A.first*p.second + B.first*A.second + p.first*B.second - B.first*p.second - p.first*A.second - A.first*B.second)/2; if(Area > maxArea) {Pmax = p;maxArea = Area;flag = true;}}pair<int,int> APmax = make_pair(Pmax.first-A.first, Pmax.second-A.second);pair<int,int> PmaxB = make_pair(B.first-Pmax.first, B.second-Pmax.second);for(auto p:pts) {pair<int,int> AP = make_pair(p.first-A.first, p.second-A.second);pair<int,int> PmaxP = make_pair(p.first-Pmax.first, p.second-Pmax.second);// 找出APmax左边的点if((APmax.first * AP.second - AP.first * APmax.second) < 0) {LeftPts.push_back(p);   }// 找出PmaxB右边的点else if(PmaxB.first * PmaxP.second - PmaxP.first * PmaxB.second < 0) {RightPts.push_back(p);}}}if(flag) {res.push_back(Pmax);helper(LeftPts, A, Pmax, up, res);helper(RightPts, Pmax, B, up, res);}}
void quickHull(vector<pair<int,int>> pts, vector<pair<int,int>>&res) {// 少于三点直接返回点集合if(pts.size()<=3) {res = pts;}// 点按x坐标排序sort(pts.begin(),pts.end());// 最左边的点和最右边的点就是凸包的顶点pair<int,int> A = pts.front();pair<int,int> B = pts.back();res.push_back(A);vector<pair<int, int>> UpPts;vector<pair<int, int>> DownPts;pair<int,int> AB = make_pair(B.first-A.first, B.second-A.second);for(auto p:pts) {pair<int,int> AP = make_pair(p.first-A.first, p.second-A.second);// 找出上凸包的里面的点if((AB.first * AP.second - AP.first * AB.second) > 0) {UpPts.push_back(p);}// 找出下凸包的里面的点else {DownPts.push_back(p);}}helper(UpPts,A,B,Up,res);// 找出下凸包的顶点helper(DownPts,A,B,Down,res);res.push_back(B);
}
int main() {vector<int> xpts = {0, 1, 5, 12, 17, 21, 24, 6, 19, 3, 9, 15, 7, 11, 6, 16};vector<int> ypts = {4, 9, 11, 12, 12, 9, 7, 0, 1, 8, 9, 8, 6, 6, 2, 2};vector<pair<int, int>> pts;for (int i = 0; i < xpts.size(); ++i) {pts.push_back(make_pair(xpts[i], ypts[i]));}vector<pair<int, int>> res;quickHull(pts, res);for(auto r:res) {cout << "(" << r.first << "," << r.second << ")" << endl;}system("pause");return 0;
}

绘图程序(python):

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as snsptsdata=[[0, 4],[1, 9],[5, 11],[12, 12],
[17, 12],
[21, 9],
[24, 7],
[6, 0],
[19, 1],
[3, 8],
[9, 9],
[15, 8],
[7, 6],
[11, 6],
[6, 2],
[16, 2]]
data=pd.DataFrame(ptsdata).rename(columns={0:'x',1:'y'})res=[(0,4),(24,7),(12,12),(1,9),(5,11),(17,12),(19,1),(6,0)]
res.sort()def frange(start, stop, step):x = startwhile x < stop:yield xx += stepfig, ax = plt.subplots()
sns.scatterplot(x='x',y='y',data=data)
uppts=[]
downpts=[]
pts=[]
A=res[0]
B=res[-1]
uppts.append(A)
downpts.append(A)
AB=(B[0]-A[0],B[1]-A[1])
for i in range(len(res)):AP=(res[i][0]-A[0],res[i][1]-A[1])if((AB[0] * AP[1] - AP[0] * AB[1]) > 0):uppts.append(res[i])elif((AB[0] * AP[1] - AP[0] * AB[1]) < 0):downpts.append(res[i])
uppts.append(B)
downpts.append(B)
pts=uppts+downpts
for i in range(len(pts)-1):X=np.array(list(frange(pts[i][0],pts[i+1][0],0.01)))Y=pts[i][1]+((pts[i][1]-pts[i+1][1])/(pts[i][0]-pts[i+1][0]))*(X-pts[i][0])ax.plot(X,Y,'b-')
plt.show()

绘图结果:

相关题目:
POJ1113题解

分治法解决凸包问题(快包)相关推荐

  1. 分治法解决最大子数组问题

    分治法解决最大子数组问题 参考文章: (1)分治法解决最大子数组问题 (2)https://www.cnblogs.com/Christal-R/p/Christal_R.html (3)https: ...

  2. 分治法解决组合总和问题(leetcode216)

    nums数组中元素是正整数 大问题转换为小问题 思路和分治法解决组合相同,代码也相似 分治法解决组合问题(递归)_m0_52043808的博客-CSDN博客 只不过递归出口时需要判断组合总和是否为n ...

  3. 分治法解决循环赛日程表

    分治法解决循环赛日程表 问题描述 设有n=2^k个运动员要进行羽毛球循环赛,现要设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次. (2)每个选手一天只能比赛一次. ( ...

  4. 分治法解决矩阵乘法问题

    分治法解决矩阵乘法问题 传统for循环: #include<iostream> #include<cstdio> #include <vector> #includ ...

  5. 分治法解决棋盘覆盖问题

    分治法解决棋盘覆盖问题 问题描述: 在一个2k×2k(k≥0)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格.显然,特殊方格在棋盘中出现的位置有4k中情形,因而有4k中不同的棋盘. ...

  6. 分治法解决最小套圈问题

    /*     Copyright    by ZhongMing-Bian     Jan,6,2010   */ /*             分治法解决最小套圈问题                 ...

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

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

  8. 分治法解决计算凸包问题

    清华大学的邓俊辉老师的<计算几何>公开课中,在计算凸包问题时会遇到极点法和极边法: 极点法是假设所有的点都是凸包上的点,然后根据In-triangle测试,把去除不是极点的点,时间复杂度是 ...

  9. 分治法解决最近点对问题

    问题 给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小.严格地说,最接近点对可能多于1对.为了简单起见,这里只限于找其中的一对. 原理(这段为抄袭https://blog. ...

最新文章

  1. 桌面虚拟化(八):扔掉你的电脑!电视就够了!
  2. windows server 2003 DNS 细谈系列之(二)记录类型、数据库
  3. 3dmax导出fbx ue4_Maya建模师分享干货的时间到啦,教你用UE4如何导入动画
  4. 【mysql】时间戳
  5. LUT Utility for FCPX(Luts调色文件加载工具)支持M1
  6. Kotlin学习系列(二)
  7. 第10课:图片管理模块
  8. linux中u盘驱动程序编写,Linux下的硬件驱动——USB设备(下)(驱动开发部分)...
  9. Cell Ranger
  10. 压力单位MPa、Psi和bar之间换算公式
  11. 超全回顾 | 5位抖音工程师揭秘抖音iOS基础技术(附PPT和回放视频)
  12. Google搜索又变聪明了 Baidu你还能HOLD住吗
  13. 世界计算机销量排名2015,全球电脑销量排名出炉,苹果位居第四,“榜首”为国产品牌!...
  14. 用Android和IOT检测地震波
  15. Fedora 26 安装搜狗拼音输入法 sogoupinyin
  16. 服务器迁移的两种方式浅谈
  17. 计算机视觉这个专业怎么样?
  18. 寻址方法有哪些-七种数据寻址-三种内存寻址
  19. Android-适配各国语言、屏幕尺寸、系统版本及常见适配方法总结
  20. Java(详解) 1011 A+B 和 C (15 分)

热门文章

  1. 国产芯片价格超低在国产替代的同时大量出口,让美国芯片深感压力
  2. java-php-python-springboot智能小区物业管理系统计算机毕业设计
  3. matlab如何建立矩阵导纳,关于利用矩阵稀疏技术求解节点导纳矩阵的MATLAB编程
  4. 一篇出海必读的——跨境支付产品介绍白皮书
  5. 新浪云平台——免费建站100个(图解教程)
  6. Linux之tar安装
  7. 无聊科技正经事周刊(第6期):纯粹的程序员与必然的中年危机
  8. windows系统下定时关闭程序
  9. 洛谷P1553 数字反转(升级版)
  10. 能量谱密度 功率谱密度