凸包问题 --- 蛮力法,Graham扫描法

2023-10-27 20:10

本文主要是介绍凸包问题 --- 蛮力法,Graham扫描法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

凸包问题 --- 蛮力法,Graham扫描法

  • 问题描述
  • 蛮力法
  • Graham扫描法

问题描述

给定一个平面上n个点的集合,它的凸包就是包含所有这些点的最小凸多边形,求取满足此条件的所有点。

另外,形象生动的描述:
(1)我们可以把这个问题看作如何用长度最短的栅栏把n头熟睡的老虎围起来。
(2)也可以这样看:请把所讨论的点想象成钉在胶合板上的钉子,胶合板代表平面。撑开一根橡皮筋圈,把所有的钉子都围住,然后啪一声松开手。凸包就是以橡皮圈为边界的区域。具体示意如下图所示:

蛮力法

  • 解题思路:
    对于一个n个点集合中的两个点p1和p2,当且仅当该集合中的其它点都位于穿过这两点的直线的同一边时,它们的连线就是该集合凸包边界的一部分,简言之,p1和p2就是凸包问题中最小凸多边形的顶点。对每一对点都做一遍检验之后,满足条件的线段就构成了该凸包的边界。
    代码演示:
//凸包问题:蛮力法
#include <iostream>
#include <cstdio>
#include <climits>
#include <algorithm>
using namespace std;
struct node{int x;int y;
};
node p1[20],p2[20];
int a[20],m=0;
int jl;int getConvexPoint(node p[],int n)
{for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(i == j){continue;}//获取直线方程的三个系数double a = p[i].y - p[j].y;double b = p[j].x - p[i].x;double c = p[i].x*p[j].y - p[i].y*p[j].x;//标记所有坐标中与直线距离的最大值和最小值double max=INT_MIN;double min=INT_MAX;for(int k = 0;k < n;k++){if(k == i || k ==j){continue;}jl = a*p[k].x + b*p[k].y + c;if(jl > max){max = jl;}if(jl < min){min =jl;}}//通过判断最大值和最小值是否同号,确定所有的坐标是否在同一方向。if(min*max >= 0){p2[m++]=p1[i];}}}return 0;
}bool compare(node p1,node p2)
{return p1.x < p2.x;
}int main()
{cout << "请输入坐标数:";int n;cin >> n;for(int i=0;i<n;i++){cin >> p1[i].x >> p1[i].y;}getConvexPoint(p1,n);cout << "其中坐标有:" << endl;sort(p2,p2+m,compare);for(int i=0;i<m;i++){if(p2[i].x==p2[i+1].x && p2[i].y==p2[i+1].y)continue;printf("(%d,%d) ",p2[i].x,p2[i].y);} cout << endl;return 0;
}

Graham扫描法

  • 解题思路:
    Graham扫描的思想是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,实际上就是进行极角排序,然后对其查询使用。
    观察凸包:

    我们可以轻松看出向量p2p3是在p1p2的左边,向量p3p4在向量p2p3的左边。
    那么如果下一个向量在上一个向量的右边呢?

    由上图我们可以轻松看出,当向量p6p3在向量p2p6的右边时,p6实际上并不是凸包的顶点。
    由此我们只需要满足几个条件,我们就可以得到最终的凸包顶点:
  1. 我们必须将平面上所有的点按照逆时针方向依次排好序,保证在遍历过程中我们能够扫描所有的点,而不会遗漏。
  2. 我们必须确定p1,p2。即凸包边上相邻的两个顶点。
  3. 判断向量p2p3是否在向量p1p2的左边。

如果满足以上条件的话,我们就可以通过遍历一个一个的把所有的凸包顶点找出来。

思路过程:
定义两个结构体数组p1[],p2[];p1[]用来存放所有坐标,p2[]用来存放凸包的顶点坐标。

  1. 把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,如图中的P0。
  2. 计算各个点相对于 P0 的幅角 α ,按从小到大的顺序对各个点排序。当 α 相同时,距离 P0 比较近的排在前面。例如上图得到的结果为 P1,P2,P3,P4,P5,P6,P7,P8。我们由几何知识可以知道,结果中第一个点 P1 和最后一个点 P8 一定是凸包上的点。 (以上是准备步骤,以下开始求凸包)
  3. 我们已经知道了凸包上的第一个点 P0 和第二个点 P1(已经满足第1,2个条件)。
  4. 至于第3个条件,我们可以借助一下数学思想。(叉积):当两个向量的叉积大于等于零时,则满足条件。反之则不满足条件。
  5. 通过遍历依次判断每个坐标,当向量p2p3在向量p1p2右边时(如上图所示),我们需要回溯将之前存储在p2[]中的p2点删除,并将p3添加到p2[]中。
  6. 就这样一直遍历下去。直到回到p0;

代码演示:

//凸包问题:扫描法
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct node{int x;int y;
};
node p1[1000],p2[1000];
int number = 0;int cross(node a,node b,node c)//计算叉积,判断c在ab上还是下
{return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}double distance(node a,node b)
{return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}//排序查找第一个点
bool compare1(node a,node b)
{if(a.y != b.y){return a.y < b.y;}else{return a.x < b.x;}
}//根据极角排序
bool compare2(node a,node b)
{int x = cross(p2[0],a,b);if(x > 0){return 1;   }else if(x == 0){// 当向量fa和向量ab共线时,为避免影响后面判断,将a,b按照到f的距离排序return distance(p2[0],a) - distance(p2[0],b) <= 0;}else{return 0;}
}int getConvexPoint(node p[],int n)
{for(int i=2;i<n;i++){while(cross(p2[number-2],p2[number-1],p1[i])<0){number--;}p2[number++] = p1[i];}return 0;
}int main()
{cout << "请输入坐标数:";int n;cin >> n;for(int i=0;i<n;i++){cout << "请输入第" << i+1 << "个坐标:  " ;cin >> p1[i].x >> p1[i].y;}// 如果只有1-3个坐标,只需输出所有坐标,因为至少3点决定一个平面。if(n <= 3){cout << "坐标有:" << endl;for(int i=0;i<n;i++){printf("(%d,%d) ",p1[i].x,p1[i].y);}return 0;}//按照y排序,找到最下面的那个点,设为fsort(p1,p1+n,compare1);//p2[0]为最开始的第一个点。p2[number++] = p1[0];//按照极角排序。sort(p1+1,p1+n,compare2);p2[number++] = p1[1];getConvexPoint(p1,n);cout << "坐标有:" << endl;for(int i=0;i<number;i++){printf("(%d,%d) ",p2[i].x,p2[i].y);}cout << endl;return 0;
}

这篇关于凸包问题 --- 蛮力法,Graham扫描法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

如何解决mysql出现Incorrect string value for column ‘表项‘ at row 1错误问题

《如何解决mysql出现Incorrectstringvalueforcolumn‘表项‘atrow1错误问题》:本文主要介绍如何解决mysql出现Incorrectstringv... 目录mysql出现Incorrect string value for column ‘表项‘ at row 1错误报错

如何解决Spring MVC中响应乱码问题

《如何解决SpringMVC中响应乱码问题》:本文主要介绍如何解决SpringMVC中响应乱码问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC最新响应中乱码解决方式以前的解决办法这是比较通用的一种方法总结Spring MVC最新响应中乱码解

pip无法安装osgeo失败的问题解决

《pip无法安装osgeo失败的问题解决》本文主要介绍了pip无法安装osgeo失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 进入官方提供的扩展包下载网站寻找版本适配的whl文件注意:要选择cp(python版本)和你py

解决Java中基于GeoTools的Shapefile读取乱码的问题

《解决Java中基于GeoTools的Shapefile读取乱码的问题》本文主要讨论了在使用Java编程语言进行地理信息数据解析时遇到的Shapefile属性信息乱码问题,以及根据不同的编码设置进行属... 目录前言1、Shapefile属性字段编码的情况:一、Shp文件常见的字符集编码1、System编码

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

Vue 调用摄像头扫描条码功能实现代码

《Vue调用摄像头扫描条码功能实现代码》本文介绍了如何使用Vue.js和jsQR库来实现调用摄像头并扫描条码的功能,通过安装依赖、获取摄像头视频流、解析条码等步骤,实现了从开始扫描到停止扫描的完整流... 目录实现步骤:代码实现1. 安装依赖2. vue 页面代码功能说明注意事项以下是一个基于 Vue.js