WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
algo09 贪心算法 — exercise.py 练习指南
练习目标
通过实现三个贪心相关的练习——石子合并、区间覆盖、贪心正确性验证,深入理解贪心算法的设计、实现和正确性分析方法。
预备知识
在开始练习前,确保你已经理解了以下概念(参见 demo.py 代码详解 中的详细解释):
- 贪心选择性质:每一步的局部最优选择不会破坏全局最优性
- 最优子结构:问题的最优解包含了子问题的最优解
heapq模块的基本用法:heapify(),heappush(),heappop()- 活动选择问题的贪心策略(按结束时间排序)
任务清单
任务1:石子合并最小代价 min_merge_cost(arr)
- 问题描述:将
堆石子合并为一堆,每次可合并任意两堆,代价为两堆之和。求最小总合并代价。 - 贪心策略:每次选最小的两堆合并(与 Huffman 编码的做法完全一致)。
- 实现步骤:
- 将输入数组转为最小堆:
heapq.heapify(heap) - 循环直到只剩 1 堆:
a = heapq.heappop(heap)— 取最小堆b = heapq.heappop(heap)— 取次小堆cost = a + b— 合并代价total_cost += cost— 累加总代价heapq.heappush(heap, cost)— 将合并后的新堆放回
- 将输入数组转为最小堆:
- 期望输出:
arr = [4, 3, 2, 6]→ 总代价 = 29- 步骤: 2+3=5 (累计5) → 4+5=9 (累计14) → 6+9=15 (累计29)
任务2:区间覆盖 min_intervals_to_cover(intervals, target_range)
- 问题描述:用最少的区间完全覆盖目标区间
。 - 贪心策略:在已覆盖的右边界以内,选择能延伸到最远的区间。
- 算法框架:
- 将所有区间按左端点升序排列
- 维护
current_end= 当前已覆盖到的右边界(初始为 L) - 在
current_end < R时循环:- 找到所有左端点 ≤
current_end的区间中,右端点最大的那一个 - 如果找不到能推进
current_end的区间 → 无法覆盖,返回 -1 - 否则选择该区间,更新
current_end
- 找到所有左端点 ≤
- 注意:原 exercise.py 中有一行
i += 1位置有 bug(在 while 循环的每次迭代中重复递增了),请修正为在遍历区间时正确递增。
任务3:验证贪心找零正确性 verify_greedy_coin_system(coins, max_amount)
- 目标:判断给定的面额系统下,贪心找零对
到 的所有金额是否都是最优解。 - 实现方法:
- 用 DP 基准算法计算出所有金额的真实最优解(最少硬币数)
- 用贪心算法计算每个金额的结果
- 比较两者,记录所有不一致的金额
- DP 基准:
dp[0] = 0 INF = max_amount + 1 (或 float('inf')) for i in range(1, max_amount+1): dp[i] = min(dp[i-coin] + 1 for coin in coins if i >= coin) - 贪心算法:每次取最大面额,用
//取整和%取余。
提示
- 堆操作:Python 的
heapq默认是最小堆(小顶堆)。heappop()总是返回最小的元素。 - 区间覆盖:先排序是关键,排序后才能用贪心高效扫描。
- 贪心正确性:失败案例通常出现在小面额上——测试 max_amount=100 基本能覆盖所有常见非规范系统的反例。
源码位置
clone 后打开(相对仓库根目录):
docs/algorithms/strategy/greedy/code/exercise.py