贪心算法(greedy algorithm,又称贪婪算法)详解(附例题)

目录
    • 基本思想
    • 一)概念
    • 二)找出全局最优解的要求
    • 三)求解时应考虑的问题
    • 四)基本步骤
    • 五)贪心策略选择
    • 六)实际应用
      • 1.零钱找回问题
      • 2.背包问题
      • 3.哈夫曼编码
      • 4.单源路径中的Djikstra算法
      • 5.最小生成树Prim算法

基本思想

贪心算法(Greedy Algorithm)是一种在求解问题时,每一步都选择当前最优解,以期望最终得到全局最优解的算法思想。贪心算法的基本思想可以总结为“每一步都做出一个局部最优的选择,最终就能得到全局最优解”。

贪心算法通常包含以下关键步骤:

找到可选的子问题: 首先,将原问题拆分成一系列可选的子问题或决策。

找到局部最优解: 对每个子问题,找到一个局部最优解。这个局部最优解应该是一个贪心选择,即在当前状态下选择最优的方式。

合并子问题的解: 将各个子问题的局部最优解合并起来,得到原问题的解。

检查解的有效性: 最后,检查得到的解是否满足问题的约束和要求。如果满足,就认为得到了问题的解。

贪心算法适用于一些特定类型的问题,通常要求问题具有贪心选择性质(即每一步的选择都是最优的),以及最优子结构性质(即问题的最优解可以通过子问题的最优解推导得出)。然而,贪心算法不一定能够求解所有问题,有些问题可能需要更复杂的算法来解决。

经典的贪心算法问题有找零钱问题、活动选择问题、背包问题中的部分背包等。贪心算法在求解这些问题时,通常能够得到接近最优解的结果,但并不保证一定能够得到全局最优解。

总之,贪心算法是一种基于每一步的局部最优选择来求解问题的思想,适用于一些满足贪心选择性质和最优子结构性质的问题。

一)概念

贪心算法(Greedy Alogorithm)又叫登山算法,它的根本思想是逐步到达山顶,即逐步获得最优解,是解决最优化问题时的一种简单但是适用范围有限的策略。

贪心算法没有固定的框架,算法设计的关键是贪婪策略的选择。贪心策略要无后向性,也就是说某状态以后的过程不会影响以前的状态,只与当前状态有关。

贪心算法是对某些求解最优解问题的最简单、最迅速的技术。某些问题的最优解可以通过一系列的最优的选择即贪心选择来达到。但局部最优并不总能获得整体最优解,但通常能获得近似最优解

在每一步贪心选择中,只考虑当前对自己最有利的选择,而不去考虑在后面看来这种选择是否合理。

二)找出全局最优解的要求

在遇见问题时如何确定是否可以使用贪心算法解决问题,那么决定一个贪心算法是否能找到全局最优解的条件是什么呢?其实就是以下两点:

  • 最优子结构(optimal subproblem structure,和动态规划中的是一个概念)
  • 最优贪心选择属性(optimal greedy choice property)

三)求解时应考虑的问题

1.候选集合S
为了构造问题的解决方案,有一个候选集合C作为问题的可能解,问题的最终解均取自于候选集合C。
2.解集合S
随着贪心选择的进行,解集合不断扩展,直到构成一个满足问题的完整解。
3.解决函数solution
检查解集合是否构成问题的完整解。
4.选择函数select
即贪心策略,这是贪心算法的关键,它指出哪个候选对象有希望构成成问题的解。
5.可行函数feasible
检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。

四)基本步骤

贪心算法使用基本步骤:
1.从问题的某个初始解出发
2.采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个部分解,缩小问题的范围或规模。
3.将所有的部分解综合起来,得到问题的最终解。

五)贪心策略选择

贪心算法的原理是通过局部最优来达到全局最优,采用的是逐步构造最优解的方法。在每个阶段,都做出一个看上去最优的,决策一旦做出,就不再更改。

要选出最优解可不是一件容易的事,要证明局部最优为全局最优,要进行数学证明,否则就不能说明为全局最优。

很多问题表面上看来用贪心算法可以找到最优解,实际上却把最优解给漏掉了。这就像现实生活中的“贪小便宜吃大亏”。所以我们在解决问题的时候,一定要谨慎使用贪心算法,一定要注意这个问题适不适合采用贪心算法

贪心算法很多时候并不能达到全局最优,为什么我们还要使用它呢?

因为在很多大规模问题中,寻找最优解是一件相当费时耗力的事情,有时候付出大量人力物力财力后,回报并不与投入成正比。在这个时候选择相对最优的贪心算法就比较经济可行了。有的问题对最优的要求不是很高,在充分衡量付出和回报后,选择贪心算法未尝不是一种不错的选择呢。

六)实际应用

1.零钱找回问题

这个问题在我们的日常生活中就更加普遍了。假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?用贪心算法的思想,很显然,每一步尽可能用面值大的纸币即可。在日常生活中我们自然而然也是这么做的。在程序中已经事先将Value按照从小到大的顺序排好。
下面展示一些 内联代码片

#include<iostream>
#include<algorithm>
using namespace std;
const int N=7; 
int Count[N]={3,0,2,1,0,3,5};
int Value[N]={1,2,5,10,20,50,100};
  
int solve(int money) 
{
	int num=0;
	for(int i=N-1;i>=0;i--) 
	{
		int c=min(money/Value[i],Count[i]);
		money=money-c*Value[i];
		num+=c;
	}
	if(money>0) num=-1;
	return num;
}
 
int main() 
{
	int money;
	cin>>money;
	int res=solve(money);
	if(res!=-1) cout<<res<<endl;
	else cout<<"NO"<<endl;
}

2.背包问题

在 从零开始学动态规划中我们已经谈过三种最基本的背包问题:零一背包,部分背包,完全背包。很容易证明,背包问题不能使用贪心算法。然而我们考虑这样一种背包问题:在选择物品i装入背包时,可以选择物品的一部分,而不一定要全部装入背包。这时便可以使用贪心算法求解了。计算每种物品的单位重量价值作为贪心选择的依据指标,选择单位重量价值最高的物品,将尽可能多的该物品装入背包,依此策略一直地进行下去,直到背包装满为止。在零一背包问题中贪心选择之所以不能得到最优解原因是贪心选择无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。在程序中已经事先将单位重量价值按照从大到小的顺序排好。

#include<iostream>   
using namespace std;   
const int N=4;  
void knapsack(float M,float v[],float w[],float x[]);  
  
int main()  
{  
    float M=50;
	//背包所能容纳的重量   
    float w[]={0,10,30,20,5};
	//每种物品的重量  
    float v[]={0,200,400,100,10};  
  	//每种物品的价值 
    float x[N+1]={0};  
    //记录结果的数组 
    knapsack(M,v,w,x);  
    cout<<"选择装下的物品比例:"<<endl;  
    for(int i=1;i<=N;i++) cout<<"["<<i<<"]:"<<x[i]<<endl;  
}  
  
void knapsack(float M,float v[],float w[],float x[])  
{  
    int i;  
    //物品整件被装下  
    for(i=1;i<=N;i++)
    {  
        if(w[i]>M) break;   
        x[i]=1;  
        M-=w[i];  
    }   
    //物品部分被装下  
    if(i<=N) x[i]=M/w[i];   
} 

3.哈夫曼编码

假设有一系列的字符,我们希望用一些二进制码来代替这些字符以进行数据压缩,使得压缩后的总比特数最小。哈夫曼编码正是这样一样压缩数据的方式。

在这里插入图片描述

如果我们已知各字符在文本中的出现频率,考虑到为了让压缩后的数据更小,我们直觉是让出现频率高的字符用尽可能短的编码,而出现频率高的则可以用更长的编码。

哈夫曼编码的解决方案是这样的:不断找到当前出现频率最小的两个结点(字符或频率),将它们结合,作为一个新生成的结点的左右子结点,并将新生成的结点继续放入比较,直到没有落单的字符。

过程演示

该贪心算法针对这个问题得到的解是最优的。

4.单源路径中的Djikstra算法

求A到其他节点的最短路径:
在这里插入图片描述
维护三个东西,从A到其他节点的路径长度队列Queue,数组visited用于记录已保存最短路径的节点,数组res用于记录节点A到其他节点的最短路径。
开始时,Queue中只有A节点自己,三组数据如下:
Queue:[(A, 0)] 起始节点为A,A到A的距离为0
visited:[true, false,false,false,false] A节点是已经访问过的节点,是true,其他节点是false
res:[0,∞,∞,∞,∞] A到自己的距离是0,到其他节点的距离目前是∞

将以A为起点的路径加入到Queue中,2和4是节点D和B的路径权重:
Queue:[(D, 2), (B, 4)]
visited:[true, false,false,false,false]
res:[0,∞,∞,∞,∞]
在Queue中,路径最短的是D,取出D,更新三组数据:
Queue:[(B, 3), (C, 3), (E, 9)]

因为A-D-B的路径权重为3小于A-B的路径权重4,所以更新一下B的路径权重。
visited:[true,false,false,true,false]
res: [0,∞, ∞,2,∞]

取出B,更新三组数据:
Queue: [(C,3), (E, 9)]
visited: [true,true,false,true,false]
res: [0,3, ∞,2, ∞]

取出C,更新三组数据:
Queue:[(E, 6)]
visited: [true,true,true,true,false]
res: [0,3, 3,2, ∞]

取出E,更新三组数据:
Queue:[]
visited: [true,true,true,true,true]
res: [0,3, 3,2, 6]

至此,Queue队列空,计算过程结束。

5.最小生成树Prim算法

prim算法(读者可以将其读作“普里姆算法”)用来解决最小生成树问题,其基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。可以发现,prim算法的思想与最短路径中Dijkstra算法的思想几乎完全相同,只是在涉及最短距离时使用了集合S代替 Dijkstra算法中的起点s。

在这里插入图片描述

①将地图上的所有边都抹去,只有当访问一个顶点后オ把这个顶点顶点连接的边显现(这点和Dijkstra算法中相同)。

②将已访问的顶点置于ー个巨型防护罩中。可以沿着这个防护罩连接的边去访问未到达的顶点

③在地图中的顶点V(0≤i≤5)上记录顶点V与巨型防护罩之间的最短距离(即V与每个访问的顶点之间距离的最小值)。由于在①把所有边都抹去了,因此在初始状态下只在顶点V0上标记0,而其他顶点都标记无穷大(记为INF)。为了方便叙述,在下文中某几处出现的最短距离都是指从顶点V与当前巨型防护罩之间的最短距离。

下面是行动策略:

①由于要访问六个顶点,因此将②③步骤执行六次,每次访问一个顶点(如果是n个顶点,那么就执行n次)。

②每次都从还未访问的顶点中选择与当前巨型防护罩最近的顶点(记为Vk(0≤k≤5)),使用“爆裂模式”的能力恢复这条最近的边(并成为最小生成树中的一条边),前往访问。

③访问顶点Vk后,将Vk加入巨型防护罩中,开放地图上Vk连接的所有边,并査看以Vk作为巨型防护罩连接外界的接口的情况下,能否利用Vk刚开放的边使某些还未访问的顶点与巨型防护罩的最短距离变小。如果能,则将那个最短距离覆盖到地图对应的顶点上。

另外,为了得到最小生成树的边权之和,需要在访问顶点之前设置一个初值为0的变量sum,并在攻打过程中将加入最小生成树中的边的边权累加起来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/441535.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

备考银行科技岗刷题笔记(持续更新版)

银行考试计算机部分复习 IEEE 802.11的帧格式 1.1 IEEE 802.11是什么&#xff1f; 802.11是国际电工电子工程学会&#xff08;IEEE&#xff09;为无线局域网络制定的标准。目前在802.11的基础上开发出了802.11a、802.11b、802.11g、802.11n、802.11ac。并且为了保证802.11更…

【电工学笔记】上册第一、二章

电工学 上次考试败在了单位&#xff0c;这次单位 一定要记熟。 第一章 电源或信号源的电压或电流称为激励,它推动电路工作; 由激励所产生的电压和电流称为响应。 复杂电路中,一般无法事先判断某个支路电流的 实际方向或者某个电路元件电压的实际方向 140V/4算不出总电阻的 …

thingsboard如何自定义udp-transport

0、参考netty实现udp的文章 https://github.com/narkhedesam/Netty-Simple-UDP-TCP-server-client/blob/master/netty-udp/src/com/sam/netty_udp/server/MessageDecoder.java 调试工具使用的是:卓岚TCP&UDP调试工具 1、在common\transport下面创建udp模块,仿照mqtt的创…

基于 HBase Phoenix 构建实时数仓(2)—— HBase 完全分布式安装

目录 一、开启 HDFS 机柜感知 1. 增加 core-site.xml 配置项 2. 创建机柜感知脚本 3. 创建机柜配置信息文件 4. 分发相关文件到其它节点 5. 重启 HDFS 使机柜感知生效 二、主机规划 三、安装配置 HBase 完全分布式集群 1. 在所有节点上配置环境变量 2. 解压、配置环境…

海外互联网专线主要解决企业哪些办公问题?

海外互联网专线 是一种专门为跨境企业提供的网络连接服务&#xff0c;旨在解决企业在海外办公过程中遇到的各种网络问题。海外互联网专线如何成为解决企业办公难题的利器&#xff0c;为企业提供稳定、高速的网络连接? 1、跨国远程办公&#xff1a; 随着全球化进程的加速&…

MyBatis Oracle 批量插入数据

MyBatis Oracle 批量插入数据 1.需求描述2.实现方案2.1 循环 insert 插入2.2 insert all 插入2.3 insert union all 插入 3.分析总结 系统&#xff1a;Win10 JDK&#xff1a;1.8.0_351 IDEA&#xff1a;2022.3.3 1.需求描述 在一次项目中实施过程中&#xff0c;后台需要将地区…

垃圾收集器底层算法

垃圾收集器底层算法 三色标记 在并发标记的过程中&#xff0c;因为标记期间应用线程还在继续跑&#xff0c;对象间的引用可能发生变化&#xff0c;多标和漏标的情况就有可能发生&#xff0c;这里我们引入“三色标记”来给大家解释下把Gcroots可达性分析遍历对象过程中遇到对象…

Apache的安装与目录结构

Apache的安装与目录结构 一、Apache是什么&#xff1f;Apache官网Apache下载地址 二、Apache的安装1.Apache在Windows安装1.启动“命令”窗口2.切换目录3.进行安装4.卸载命令5.启动Apache 服务 2.Apache在linux安装1.linux安装代码1.在 Fedora/CentOS/Red Hat Enterprise Linux…

Python 读取写入excel文件

使用Python读取和写入excel的xlsx、xls文件 目录 读取xlsx文件 安装三方库 引入三方库 读取数据 打开文件 表名 最大行数 最大列数 读取一张表 读取整个文件 返回xls整体内容 安装三方包 读取内容 写入xls文件 引入三方库 创建文件并写入数据 报错及解决 报错…

qt自定义时间选择控件窗口

效果如图&#xff1a; 布局如图&#xff1a; 参考代码&#xff1a; //DateTimeSelectWidget #ifndef DATETIMESELECTWIDGET_H #define DATETIMESELECTWIDGET_H#include <QWidget> #include <QDateTime>namespace Ui { class DateTimeSelectWidget; }class DateTim…

给一篇word注音可不可以只要拼音不要汉字 word中如何只保留拼音不要汉字

word中如何只保留拼音不要汉字&#xff0c;如果你想要只保留拼音而去除汉字&#xff0c;可以通过一系列步骤来实现。以下是一个详细的教程&#xff0c;帮助你完成这个任务。 首先&#xff0c;确保你的电脑已经安装了“汇帮注音大师”软件。如果没有&#xff0c;你需要安装一下…

仿12306校招项目业务五(敏感信息模块)

加密存储 数据加密背景 数据加密是指对某些敏感信息通过加密规则进行数据的变形&#xff0c;实现敏感隐私数据的可靠保护。 涉及客户安全数据或者一些商业性敏感数据&#xff0c;如身份证号、手机号、卡号、客户号等个人信息按照相关部门规定&#xff0c;都需要进行数据加密。…

app逆向-ratel框架-sekiro框架的安装使用

文章目录 一、前言二、初次尝试三、案例测试 一、前言 sekiro主要支持多节点的程序调用&#xff0c;所以他归属于RPC&#xff08;Remote Procedure Call&#xff09;框架&#xff1a;API管理、鉴权、分布式、负载均衡、跨语言 开源文档&#xff1a;https://sekiro.iinti.cn/s…

python使用Open3D 对点云重建与c++使用PCL对点云进行重建哪个效率更高

确定哪个库更高效取决于多个因素&#xff0c;包括算法实现、优化程度、硬件配置等。通常情况下&#xff0c;C 的 PCL 库在性能方面可能会比 Python 的 Open3D 库更高&#xff0c;因为 C 语言的编译器可以生成更高效的机器码&#xff0c;并且 PCL 库的底层实现是经过高度优化的。…

C++:模版进阶 | Priority_queue的模拟实现

创作不易&#xff0c;感谢三连支持 一、非类型模版参数 模板参数分类为类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&…

AIGC启示录:深度解析AIGC技术的现代性与系统性的奇幻旅程

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

LayerNorm的图是不是画错了

这是网上一张很流行的说明几个 Normalization 区别的图 这图出自Kaiming的文章 Group Norm 但是他这个 Layer Norm 的图是不是画错了? 我大四写毕设的时候就想问&#x1f923;&#x1f923;&#x1f923; 这都几年过去了 我觉得图应该是这样画的&#xff0c;相同颜色的区域…

HifiFace: 3D形状和语义先验引导高保真人脸交换阅读笔记

HifiFace: 3D Shape and Semantic Prior Guided High Fidelity Face Swapping HifiFace: 3D形状和语义先验引导高保真人脸交换 介绍 可以很好地保留源人脸的脸型&#xff0c;并产生逼真的结果。不同于现有的人脸交换只使用人脸识别模型来保持身份相似性的方法&#xff0c;我们…

【万题详解】DFS搜索专题合集(上)

专栏推荐 我的专栏——专栏链接 1.文章平均质量分 70分以上 2.以洛谷题为基础&#xff0c;解决C问题 3.有题目、讲解、思路、参考代码…… 4. 文章数&#xff1a;29 &#xff08;2024.3.8&#xff09; 课前C小程序&#xff08;脱控极域电子教室&#xff09; 这个图标相信…

C语言分析基础排序算法——选择排序

目录 选择排序 选择排序 堆排序 选择排序 选择排序 选择排序的基本思路是&#xff0c;定义两个区间指针begin和end&#xff0c;遍历数组中的每一个数据找出最大的数据的下标和最小的数据的下标&#xff0c;之后与begin和end指针分别交换小数据与begin的位置以及大数据和e…