Greedy
贪心算法是一个非常常见的算法,根据字面意思,贪心就是贪婪,因此在贪心算法中,我们抛弃了大局观,转而去关注每一个局部子问题,对每一个局部子问题去求最优解,最后把所有的局部最优解结合起来作为我们最终的全局解。那么很直观的一个问题就是,局部最优解的结合很多时候并不一定是全局最优解,因此贪心算法是存在局限性的,不能去处理“最大”或者“最小”问题。作为一个 A I e r AIer AIer,贪心算法与我们的梯度下降算法很像,即有时候我们得到的只是局部最优,但有时候,我们也能得到全局最优或者全局最优解的近似解。
贪心算法在OI中有几个非常经典的题型,我们分别来总结一下:
Sort
排序是贪心问题中最常用的一种方法,而只需要直接排序的题目其实贪心策略是比较好想的。比较典型的问法我们在这里列举几道题:
- 现在已知 n 个苹果到达地上的高度 x i x_i xi,椅子的高度 a a a,陶陶手伸直的最大长度 b b b,陶陶所剩的力气 s s s,陶陶摘一个苹果需要的力气 y i y_i yi,求陶陶最多能摘到多少个苹果
- 给出 Marry 乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量。计算采购足够数量的牛奶所需的最小花费。
- 每组纪念品的价格之和不能超过一个给定的整数,为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
诸如此类的问题,共性是都要求花费最少或者收益最大,都是可以直接排序求解。一般来说,我们可以根据题目中被限制的变量进行排序,如第一题中的**“力气”,第二题中的"单价",第三题中的"价格"**。
Compare+Sort
最典型的例题就是国王的游戏,这类题目通常是通过列举 i i i和 j j j,分别写出 i i i在 j j j前或 j j j在 i i i前的损失或收益,然后根据题意进行比较,于是就能确定整个序列的排序依据。举例来说,假设 i i i在 j j j前的最大收益为 a i / b j a_i / b_j ai/bj, j j j在 i i i前的最大收益是 a j / b i a_j/b_i aj/bi,要求收益最大化,因此我们希望 a i b j ≥ a j b i \frac{a_i}{b_j} \ge \frac{a_j}{b_i} bjai≥biaj,因此变形以后就得到我们应该按照 a i ∗ b i a_i * b_i ai∗bi尽心排序。
Interval Cover
最大不相交区间数
这类问题就是给定 n n n个区间 [ l i , r i ] [l_i, \ r_i] [li, ri],且所选择的区间不能相交,即若 [ l i , r i ] [l_i, \ r_i] [li, ri]和 [ l j , r j ] [l_j, \ r_j] [lj, rj]同时被选,则需要保证 r i ≤ l j r_i\le{l_j} ri≤lj或者 r j ≤ l i r_j\le{l_i} rj≤li。问最多能选择多少个不重合的区间。这类问题的贪心策略是按照区间右端点进行排序,然后顺序遍历,挑选出所有不重合的区间。
Greedy with Binary Search
贪心+二分答案也是非常常见的一类问题,具体的流程是:二分要求的答案,然后用当前的答案去进行判断看是否能够满足题意。判断的过程往往需要进行贪心。这类问题还有一个很显著的特点是题目的描述中经常会有求最大值最小或最小值最大,出现这种字眼时往往就是贪心和二分的结合。