【算法导论】学习笔记——第16章 贪心算法
贪心算法是使做出的选择看起来都是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解。其实,这个定义旨在说明贪心算法必须伴随做出最优选择,如moving table,我们选择最大重叠数等等。当然,很多情况下,并不一定局部最优解就是全局最优解。与DP类似,很多贪心算法设计时都很容易想到递归的形式,然而由于深度太深,很容易stack overflow。因此,这时候,大多我们都需要将递归变为递推。以前,不是很会转化。现在,觉得很容易。递归是自顶向下求解,而递推则是自底向上求解。一般递推都需要伴随着表来实现。
1. 在开发一个贪心算法时,我们一般要遵循如下几个步骤:
(1)设计问题的最优子结构
(2)设计出一个递归解
(3)证明在递归的任一阶段,最优选择之一总是贪心选择。那么,做贪心选择总是安全的
(4)设计出一个实现贪心策略的递归算法
(5)将递归算法转换成迭代算法(即将递归转化为递推)
通过这些步骤,可以看出动态规划与贪心很类似。其实很多问题都看似可以贪心,实则无法满足最优解的条件,只能DP。正如作者所说,在构造贪心时,我们可以会直接作出贪心选择,很多时候,贪心选择很明显且易构造。对我而言,难点往往是证明贪心的合理性。
2. 贪心选择性质
贪心选择性质是贪心算法的关键点之一,一个全局最优解可以通过局部最优(贪心)选择来实现,才满足贪心条件。也就是说,当考虑做出选择时,我们总是基于当前情况做出最优选择,我们认为通过当前最优选择可以获得全局最优解。而在动态规划中,当前的选择与之前的选择很可能相关,动态规划在这一点上更类似于一个状态机进行状态转移,逐渐转移到最优状态,但在当前某一步并不一定是当前的最优状态。很多时候可以通过对数列排序等对子问题进行选择,从而降低问题复杂度。
3. 最优子结构
对于一个问题来说,如果它的一个最优解包含了其子问题的最优解,则称该问题具有最优子结构。在贪心算法中使用最优解时,通常很直接。假设在原问题中做了贪心选择而得到了一个子问题,那么需要证明此子问题的最优解与所做的贪心选择合并后,可以得到原问题的一个最优解,即最终的最优解包含子问题的最优解。通常采用归纳法,证明每个步骤中所做的贪心选择最终会产生一个最优解。
4. 贪心算法与动态规划
有两个经典问题可以举例表明二者的区别,0/1背包问题和部分背包问题。对于0/1背包问题,由于可能存在背包没填满的情况,剩余的空间体积降低了物品的价值。当考虑一件物品是否该加入背包时,必须把该物品加进去的子问题的解与不取该物品的子问题的解进行比较。而这种比较形成了很多子问题重叠,因此可以用动态规划解决。然而对于部分背包问题,如杭电2111通过单位体积的物品价值排序,每次总是价值最大的装填,可以保证在装填任意物品后,背包满,且价值最大。显然最优选择是尽量获取有剩余的价值最高的物品。很容易看出二者的区别。