📘 教案 05:贪心算法(Greedy Algorithm)
1. 问题定义
贪心算法用于解决一类问题:
在每一步做"当前最优选择",期望得到全局最优解
2. 核心思想
局部最优 → 全局最优
3. 核心术语
3.1 贪心选择(Greedy Choice)
定义:
在当前状态下,选择"看起来最优"的决策
3.2 全局最优(Global Optimum)
定义:
问题的最终最优解
3.3 贪心性质(Greedy Property)
定义:
局部最优选择可以导出全局最优解
👉 这是贪心算法成立的必要条件
4. 贪心 vs 动态规划(核心对比)
| 项目 | 贪心 | 动态规划 |
|---|---|---|
| 决策方式 | 每一步最优 | 综合考虑 |
| 是否回溯 | ❌ | ✅ |
| 是否保证最优 | 仅部分问题 | 所有适用问题 |
| 复杂度 | 低 | 较高 |
5. 贪心算法基本结构
text
for 每一步:
选择当前最优
👉 没有"回头"
6. 什么时候可以用贪心?
必须满足两个条件:
6.1 最优子结构(和DP一样)
6.2 贪心选择性质(关键)
当前最优选择不会影响后续最优性
7. 示例1:活动选择问题(经典)
问题:
给定多个活动:
(starti,endi)\]\[ (start_i, end_i) \]\[(starti,endi)
选择最多互不重叠的活动
贪心策略:
按结束时间最早排序,优先选结束早的
为什么成立?
- 结束越早 → 留给后面的空间越大
算法:
text
按 end 排序
选择第一个活动
for 后续活动:
if start ≥ 当前结束:
选择
时间复杂度:
O(nlogn)\]\[ O(n \\log n) \]\[O(nlogn)
8. 示例2:区间覆盖
问题:
用最少区间覆盖一个目标区间
贪心策略:
每次选择"起点 ≤ 当前点,且终点最远"的区间
本质:
👉 每一步"扩展最远"
9. 示例3:最小生成树(贪心应用)
Kruskal 算法
策略:
按边权从小到大选边,不形成环
本质:
👉 每一步选"当前最小代价"
10. 示例4:Dijkstra(单源最短路径)
条件:
所有边权非负
贪心策略:
每次选当前距离最小的点
👉 一旦确定,就不再改变
11. 反例(必须讲)
❌ 反例:零钱问题(某些情况)
币值:
text
1, 3, 4
目标:6
贪心:
text
4 + 1 + 1 = 3枚
最优:
text
3 + 3 = 2枚
👉 贪心失败
12. 为什么贪心会失败?
因为:
局部最优 ≠ 全局最优
13. 如何判断能不能用贪心?
方法1:证明(严格)
证明:
- 贪心选择不会变差
方法2:反例测试(实用)
- 构造反例
- 若失败 → 不能用贪心
14. 贪心算法常见类型
| 类型 | 策略 |
|---|---|
| 区间问题 | 选结束最早 |
| 覆盖问题 | 选延伸最远 |
| 路径问题 | 选当前最短 |
| 排序问题 | 排序后贪心 |
15. 常见错误
❌ 错误1:看到最优就用贪心
👉 必须验证贪心性质
❌ 错误2:忽略约束条件
❌ 错误3:不会构造反例
16. 关键结论
- 贪心 = 局部最优决策
- 不一定正确
- 必须证明或验证
- 优点是简单高效