贪心算法 ‌(Greedy Algorithm)

一、贪心算法是什么?

贪心算法(greedy algorithm ,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解

贪心算法总是做出当前最优选择,期望通过局部最优选择得到全局最优解。

贪心算法正是“活在当下,看清楚眼前”的算法,从问题的初始解开始,一步一步地做出当前最优选择,逐步逼近目标,尽可能得到最优解;即使得不到最优解,也可以得到最优解的近似解。

贪心算法在解决问题的策略上看似“目光短浅”,即只根据当前已有的信息做出选择,而且一旦做出选择,则不管将来有什么结果,都不会改变。换言之,贪心算法并未考虑全局最优,只是考虑了某种意义上的局部最优。

贪心算法在生活、生产中被广泛应用,对许多问题都可以使用贪心算法得到全局最优解或全局最优解的近似解。

二、贪心算法核心思想

贪心算法的核心思想理念是:‌在每一步决策中都选择当前最优的选项,通过局部最优的累积逐步逼近全局最优解‌。‌

‌核心定义在每一步决策中选择当前状态下的最优解(局部最优),通过局部最优累积期望全局最优解,但不保证全局最优解
‌算法名称贪心算法(Greedy Algorithm)
关键性质贪心选择性质(局部最优含于全局最优)最优子结构
‌典型算法‌霍夫曼编码,Kruskal/Prim最小生成树,Dijkstra最短路径
适用场景活动选择问题,找零问题,区间覆盖问题
‌局限性不保证全局最优(如0-1背包问题,需严格证明正确性
‌与动态规划区别贪心:无回溯,仅当前最优 DP:存储子问题解,可能回溯

!!!贪心是DP的特例(当贪心选择性质成立时


三、贪心算法核心算法分类

基础算法‌:
  • 枚举法‌:穷举所有可能解(如质数判定)‌。
  • 模拟法‌:分步还原实际过程(如游戏逻辑实现)‌。
  • 模拟法‌:分步还原实际过程(如游戏逻辑实现)‌。
数据结构相关算法‌
  • 排序算法‌:快速排序、归并排序等‌。‌
  • 搜索算法‌:二分查找、深度优先搜索(DFS)‌。
数值与编码算法‌
  • 进制转换‌:二进制、十六进制处理‌。
  • 位运算‌:利用 &、|、<< 等优化计算效率‌。
扩展领域算法‌
  • 字符串算法‌:模式匹配(KMP)、数据压缩‌。‌
  • 并行算法‌:多线程协同处理(如金融交易系统)‌。

四、贪心算法方法论与实践

  • 问题抽象‌:将现实问题转化为可计算的数学模型(如用图论解决路径规划)‌。

  • 效率权衡‌:根据场景选择时间/空间复杂度最优的算法(如哈希表牺牲空间换时间)‌。
    (富媒体流组件将展示相关视频封面与标题,涵盖算法分类、核心概念等视觉化内容)。


五、经典的贪心算法案例

P1090 [NOIP 2004 提高组] 合并果子

1.题目

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n1 次合并之后,
就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1 ,并且已知果子的种类
数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 3 3 种果子,数目依次为 1 1 1 2 2 2 9 9 9 。可以先将 1 1 1 2 2 2 堆合并,新堆数目为 3 3 3 ,耗费体力为
3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12 ,耗费体力为 12 12 12 。所以多多总共耗费体力
= 3 + 12 = 15 =3+12=15 =3+12=15 。可以证明 15 15 15 为最小的体力耗费值。

输入格式

共两行。 第一行是一个整数 n ( 1 ≤ n ≤ 10000 ) n(1\leq n\leq 10000) n(1n10000) ,表示果子的种类数。

第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1\leq a_i\leq 20000) ai(1ai20000) 是第 i i i
种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231

输入输出样例 #1
输入 #1

3 1 2 9

输出 #1

15

说明/提示

对于 30 % 30\% 30% 的数据,保证有 n ≤ 1000 n \le 1000 n1000

对于 50 % 50\% 50% 的数据,保证有 n ≤ 5000 n \le 5000 n5000

对于全部的数据,保证有 n ≤ 10000 n \le 10000 n10000

2.思路

核心思想‌
  • 贪心策略‌
    这就是一道典型的贪心题。每次选择‌重量最小的两堆‌进行合并,将合并后的新堆重新放入待合并队列中‌。如图所示:
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

局部最优‌:每次合并的体力消耗最小。
‌全局最优‌:多次局部最优累积得到最小总代价‌。

  • 数据结构选择‌
    使用‌小顶堆(优先队列)‌高效获取最小值,时间复杂度为 ‌O(n log n)‌‌。

特殊知识点(除贪心外)

  • 为什么每次选择‌重量最小的两堆‌进行合并?
    解释起来太复杂,跟如何构建一棵哈夫曼树有关系,可以自己了解一下。

  • 小顶堆是什么?
    小顶堆就是每个结点的值都小于或等于其左右孩子结点的值的堆。
    可以看看这篇文章了解一下堆排序(浅谈大顶堆与小顶堆)


3.代码片段

变量定义

#include <bits/stdc++.h>
using namespace std;
priority_queue<int, vector<int>, greater<int> > q; //定义小顶堆q 
int n, a, ans;
//n:输入的数字个数
//a:临时存储每次输入的数字
//ans:累计合并的总代价

读入

	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a;
		q.push(a); //输入入队
	}

核心代码(小顶堆的使用和贪心思想的表现)

	while (q.size()>1){  //停止循环条件
		int t=q.top(); //记录队首
		q.pop();   //删除队首(多余的,合并后多的)
		ans+=q.top()+t; //记录累加代价
		q.push(q.top()+t); //将当前堆顶元素q.top()与之前取出的最小值t相加后,将结果重新放入队列中
		q.pop(); //删除多余的,合并剩的
	}
  • 局部最优选择‌
    每次循环总是取出当前最小的两个元素(q.top()和t)进行合并,确保每次合并的代价最小。这种“每次选择最小合并对”的策略正是贪心算法的核心特征——通过局部最优选择推进全局最优解。

  • 问题分解与迭代‌
    将合并后的新值重新放入优先队列,继续参与后续合并。这一过程保持了问题的“最优子结构”性质,即当前步骤的局部最优解(最小合并代价)会直接影响后续步骤的决策,最终累积为全局最优解。

输出

	cout<<ans;//输出 

4.附上AC代码

#include <bits/stdc++.h>
using namespace std;
priority_queue<int, vector<int>, greater<int> > q; //定义小顶堆q 
int n, a, ans;
//n:输入的数字个数
//a:临时存储每次输入的数字
//ans:累计合并的总代价
int main(){
	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a;
		q.push(a); //输入入队
	}
	while (q.size()>1){  //停止循环条件
		int t=q.top(); //记录队首
		q.pop();   //删除队首(多余的,合并后多的)
		ans+=q.top()+t; //记录累加代价
		q.push(q.top()+t); //将当前堆顶元素q.top()与之前取出的最小值t相加后,将结果重新放入队列中
		q.pop(); //删除多余的,合并剩的
	}
	cout<<ans;//输出 
	
	return 0;
}

有感觉了吗?领悟到贪心的真谛了吗?如果还没有,可以写写这个题单


六、贪心算法到底是什么?

想象你在解决一个大问题时,把它拆成很多个小步骤。‌贪心算法就是:在每一步,你都只看眼前,选择当下看起来最好的那个选项,不考虑以后的结果。‌ 然后就这么一步步做下去,直到问题解决。

打个通俗的比方:
  • ‌问题:‌ 你想用最少的硬币凑够 10 块钱(比如有 1元、2元、5元、10元硬币)。
    ‌贪心的做法:‌ 每次都选 ‌当前‌ 能用的最大面额的硬币。
    第一步:选 10元硬币?太大了(10 > 10?不对,10是刚好),不行,选最大的——10元硬币。但10元硬币刚好等于10元,所以直接选10元硬币,一步解决。
    如果数额是13元,你会:先用10元(剩下3元),再用2元(剩下1元),最后用1元。总共用了3个硬币(10+2+1)。这是最优解。
    ‌为什么叫“贪心”?‌ 因为它很“贪心”,每一步都想马上占到最大的便宜(用掉最大的钱),不管后面会不会吃亏。它只顾眼前利益。

  • ‌贪心算法的关键特点:‌
    ‌目光短浅:‌ 只考虑当前这一步最好的选择,不往后看未来几步会发生什么,也不回头修改以前的决定。
    ‌简单粗暴:‌ 通常思路简单,执行速度快。
    ‌不保证全局最优:‌ ‌这是最重要的!‌ 因为它只顾眼前,最终得到的结果 ‌不一定是最好的全局结果‌。有时候这种“只顾眼前”的做法会导致最终结果变差。
    ‌只在特定问题有效:‌ 贪心算法只有在一种特定情况下才能保证得到最优解:那就是 ‌你每一步的“局部最优”选择,最终一定能导致“全局最优”‌。很多问题不具备这个性质。


总结

贪心算法就是 ‌“走一步看一步,每一步都选当下最爽的,一路走到黑,结果好不好全看运气和问题本身”‌的一种解题策略。


如果对你理解有帮助,就点个赞再走吧,有问题欢迎随时在评论区指出。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值