1. 贪心算法基础认知第一次接触贪心算法时我盯着那个贪字琢磨了半天——这算法难道很自私吗后来在蓝桥杯真题里被反复折磨后才明白贪心算法的精髓就在于眼前最优解。就像玩俄罗斯方块时我们会优先消除最底层的方块虽然不知道下一个是什么形状但当前能做的局部最优选择往往能带来全局优势。贪心算法Greedy Algorithm的核心思想是在每一步选择中都采取当前状态下最好或最优的选择从而希望导致结果是全局最好或最优的算法。与动态规划不同它不会回溯之前的决策这也意味着算法效率通常更高。在蓝桥杯竞赛中贪心类题目约占15%-20%常见于最短路径、任务调度、区间覆盖等题型。关键理解贪心算法能解决的问题必须具有贪心选择性质局部最优能导致全局最优和最优子结构问题的最优解包含子问题的最优解。这也是面试官最爱问的考点之一。2. 贪心算法典型问题拆解2.1 区间调度问题这是蓝桥杯最常考的题型之一。比如2021年省赛真题给定多个会议的开始和结束时间如何安排才能参加最多会议解决思路是经典的最早结束优先策略将所有区间按结束时间升序排序选择第一个结束的区间排除所有与该区间重叠的区间重复步骤2-3直到没有剩余区间// Java实现代码示例 public int maxMeetings(int[][] intervals) { Arrays.sort(intervals, (a,b) - a[1] - b[1]); int count 1, end intervals[0][1]; for(int i1; iintervals.length; i){ if(intervals[i][0] end){ count; end intervals[i][1]; } } return count; }2.2 分糖果问题这类问题考察对贪心策略的灵活运用。例如有N个孩子和M块糖每个孩子有个期望值g[i]每块糖有个大小s[j]只有当s[j]g[i]时才能分配求最多满足多少孩子解题步骤将孩子期望和糖果大小分别排序用最小的糖满足最小期望的孩子双指针遍历两个数组public int findContentChildren(int[] g, int[] s) { Arrays.sort(g); Arrays.sort(s); int i 0, j 0; while(i g.length j s.length){ if(s[j] g[i]) i; j; } return i; }3. 贪心算法解题框架经过多次实战我总结出贪心问题的通用解题模板问题转化明确什么是最优解什么是局部最优选择验证性质确认问题具有贪心选择性质和最优子结构策略设计设计贪心策略这是最考验思维的部分正确性证明用反证法或数学归纳法验证策略编码实现通常需要配合排序等预处理以找零钱问题为例假设硬币无限供应问题用最少硬币数凑出指定金额策略每次选择不超过剩余金额的最大面值实现降序遍历硬币面值数组public int coinChange(int[] coins, int amount) { Arrays.sort(coins); int count 0; for(int icoins.length-1; i0; i--){ while(amount coins[i]){ amount - coins[i]; count; } } return amount 0 ? count : -1; }4. 贪心算法常见陷阱4.1 局部最优≠全局最优这是贪心算法最容易踩的坑。比如背包问题如果用贪心策略按价值密度排序得到的往往是近似解而非最优解。在蓝桥杯中这类题目通常会设置特殊测试用例来检验算法正确性。识别方法尝试构造反例看是否存在局部最优导致全局非最优的情况。4.2 预处理成本忽略很多贪心算法需要先排序时间复杂度通常是O(nlogn)。当n很大时比如1e5量级这可能成为性能瓶颈。我曾在一道题中因为用了冒泡排序O(n^2)导致超时。优化建议优先使用Java的Arrays.sort()基于TimSort对于原始类型数组使用双轴快速排序考虑是否真的需要完整排序有时部分排序即可4.3 边界条件处理贪心算法对输入数据边界特别敏感。常见需要特殊处理的情况包括空输入或零值输入所有元素相同的情况整数溢出特别是累加场景浮点数精度问题防御性编程建议// 在代码开头添加校验 if(intervals null || intervals.length 0) return 0; if(coins null || coins.length 0) return -1;5. 蓝桥杯真题实战分析5.1 2022年省赛真题最少雷达问题题目描述海岸线上有若干岛屿每个雷达覆盖范围为d求覆盖所有岛屿需要的最少雷达数。解题思路计算每个岛屿能被覆盖的雷达放置区间转化为区间选点问题类似区间调度按区间右端点排序贪心选择总是把雷达放在当前区间的右端点public int minRadars(int[][] islands, int d) { Listdouble[] intervals new ArrayList(); for(int[] island : islands){ double dx Math.sqrt(d*d - island[1]*island[1]); intervals.add(new double[]{island[0]-dx, island[0]dx}); } intervals.sort((a,b) - Double.compare(a[1], b[1])); int count 0; double last -Double.MAX_VALUE; for(double[] interval : intervals){ if(interval[0] last){ count; last interval[1]; } } return count; }5.2 2023年模拟题任务调度器题目要求给定一组任务和冷却时间n求最短执行时间。同一任务执行后需间隔n个时间单位才能再次执行。贪心策略统计每种任务的频率按频率从高到低排序安排最高频任务其他任务填充空闲时间计算公式(max_count - 1) * (n 1) 同等频率任务数public int leastInterval(char[] tasks, int n) { int[] freq new int[26]; for(char c : tasks) freq[c-A]; Arrays.sort(freq); int max freq[25] - 1; int slots max * n; for(int i24; i0 freq[i]0; i--){ slots - Math.min(freq[i], max); } return slots 0 ? tasks.length slots : tasks.length; }6. 贪心算法优化技巧6.1 优先队列的应用当需要动态获取当前最优选择时PriorityQueue是利器。比如哈夫曼编码问题中每次需要取频率最小的两个节点。示例合并果子问题public int mergeFruits(int[] fruits) { PriorityQueueInteger pq new PriorityQueue(); for(int f : fruits) pq.offer(f); int cost 0; while(pq.size() 1){ int sum pq.poll() pq.poll(); cost sum; pq.offer(sum); } return cost; }6.2 双指针技巧在很多区间类问题中双指针可以避免不必要的遍历。比如在无重叠区间问题中通过比较相邻区间的结束和开始时间来优化。public int eraseOverlapIntervals(int[][] intervals) { Arrays.sort(intervals, (a,b) - a[1] - b[1]); int count 0, prevEnd Integer.MIN_VALUE; for(int[] interval : intervals){ if(interval[0] prevEnd){ prevEnd interval[1]; }else{ count; } } return count; }6.3 预处理优化有时预处理可以大幅简化问题。比如在跳跃游戏问题中通过维护最大可达距离来避免回溯public boolean canJump(int[] nums) { int maxReach 0; for(int i0; inums.length; i){ if(i maxReach) return false; maxReach Math.max(maxReach, i nums[i]); } return true; }7. 贪心算法专项训练建议根据我的备赛经验建议按以下顺序系统训练基础题型2天区间调度类最多不相交区间、区间合并等分配类分糖果、饼干分配等简单背包类分数背包、找零钱等进阶题型3天带权区间调度多约束条件问题需要预处理的问题综合应用2天结合数据结构的贪心优先队列、并查集等需要数学推导的问题往年蓝桥杯真题每日训练量建议5道基础题30分钟内/题3道进阶题45分钟内/题1道真题模拟限时完成重要心得贪心算法的难点不在于编码而在于策略设计。建议每道题先用10分钟思考贪心策略的正确性再动手编码。我在初期经常因为急于编码而忽略策略验证导致浪费时间。