进阶贪心算法例解

本文章题目和算法本身来自《算法设计与分析》(屈婉玲版),黑书(刘汝佳),理解为本人理解。如有补充或不同见解欢迎在下方留言区讨论。

目录

  • 哈夫曼树
  • 最小生成树:Prim
  • 例:钓鱼
  • 例:照亮的山景
  • 例:过河问题

哈夫曼树

算法描述

为获得平均长度最短的编码,不断将字符集中使用频率最小的两个字符取出(不放回),合并成为一棵子树,将父节点作为一个字符放回字符集,使其频率为两个子节点的权值之和。这样构造的树是一棵提供最优编码的树,每一条从树根到树叶的路径都是一个字符的编码

核心问题

  • 为什么每次要选取两个频率最小的字符(归纳基础)
  • 证明牵扯到树的变化,而有关优化目标总是和树的高度有关,有失一般性,怎样处理?

试证

  • 对于一个最优方案对应的二叉树T,倘若我将频率最低的两个子节点放到最底层,那么我就可以把它们合并,将二叉树规模缩小,运用归纳假设构造一个最优解
  • 倘若我可以知道合并节点之后前后两棵树的关系,我就可以把这步贪心决策和这棵新树合并,证明它也有最优性。但首先需要先证明T合并之后的那棵树也是个最优的树

引理4.2

x , y x, y x,y是最优二叉编码树T 中频率最小的两个叶子节点,与最底层两个叶子节点 x x x,y$交换后不会改变树的最优性。显然有:
B ( T ) − B ( T ′ ) = [ f ( x ) − f ( a ) ] [ d T ( x ) − d T ( a ) ] + [ f ( y ) − f ( b ) ] [ d T ( y ) − d T ( b ) ] ≥ 0 B(T)-B(T^{'})=[f(x)-f(a)][d_T(x)-d_T(a)]+[f(y)-f(b)][d_T(y)-d_T(b)]\ge 0 B(T)B(T)=[f(x)f(a)][dT(x)dT(a)]+[f(y)f(b)][dT(y)dT(b)]0

引理4.3

x , y x, y x,y为二叉树T的两片叶子,设将其合并后新树为 T ′ T^{'} T,则两棵树的关系为:
B ( T ) = B ( T ) + f ( x ) + f ( y ) B(T)=B(T)+f(x)+f(y) B(T)=B(T)+f(x)+f(y)

正式证明

  • 由于不是按步骤归纳而是按规模归纳,所以第一步不是最重要的推理
  • n = 2 n=2 n=2时结论显然
  • 假设 n = k n=k n=k时成立。对于一棵有 n + 1 n+1 n+1个字符的二叉编码树,我们可以把两片频率最小的叶子 a , b a,b a,b放到最下面而不影响最优性
  • 将其合并成节点 s s s获得树T’,则可以根据贪心算法用T‘的叶子节点的权值构造最优二叉编码树T*
  • T’也是一棵最优二叉编码树,否则B(T’)>B(T*),两边同时加f(a),f(b),左边变成B(T),右面是将 a , b a,b a,b填到 s s s下面得到的新树,显然T的最优性失效,矛盾
  • 所以把 a , b a,b a,b填到 s s s下面构成的新树的平均编码长度与最优编码相等,它也是个最优编码:使用贪心想法构成的最优编码

套路总结

对于归纳问题规模型贪心,有以下套路:

  • 对于规模为 ( n + 1 ) (n+1) (n+1)的问题,证明贪心操作所涉及的元素不会影响到最优性(如挪节点)
  • 合并,缩小问题规模为 T ′ T' T,用归纳假设:规模为 n n n时可用贪心构造 T ∗ T^* T
  • 证明 T ′ T' T也是最优解,因为 T ′ T' T T ∗ T^* T同规模,故展开后也同规模,故用贪心构造出的解对于 ( n + 1 ) (n+1) (n+1)规模也是最优解

讲的很抽象,多练习。

最小生成树:Prim

算法综述

  • 将图G的顶点分为两个集合:处理过的 S S S和未处理过的 T − S T-S TS
  • 选择连接 S S S T − S T-S TS的最短的边 e e e,并将 T − S T-S TS侧的与 e e e相连的点加入到 S S S
  • 重复以上算法,直至 T − S T-S TS为空

证明思路

  • 与上一题不同,本题采用对贪心的步骤进行归纳,而非问题的规模
  • 证明的关键在于第一步:使用第一步的证明作为后续子问题的归纳基础
  • 通用思路:假设存在使第k步成立的最优方案,对于问题中剩下未处理的部分证明它必须用最优子问题的解法与前k步成立才能得到最终的最优方案
  • 利用归纳基础:子问题的第一步可以用贪心算法,故构造了一个包含前k+1步贪心决策的最优决策。

证明实战

  • 第一步必须是选择权值最小的边 e 1 = ( 1 , i ) e_1=(1, i) e1=(1,i),否则若使用的是 e 1 l , w ( e 1 l ) > w ( e 1 ) e_1^l, w(e_1^l)>w(e_1) e1l,w(e1l)>w(e1),在最终生成的树 T T T中若加入 e 1 e_1 e1则生成含 e 1 , e 1 l e_1, e_1^l e1,e1l的圈,破除 e 1 l e_1^l e1l后剩下的仍是一棵生成树,但总长下降,与最优性矛盾
  • 假设存在最小生成树 T T T包括前k步选择的边,构成了已处理点集 S S S和未处理点集 V − S V-S VS,根据最小生成树的定义, T T T的结构必须是 S S S中已经出现的树+将 S S S看成一个大点后剩下点的生成树合并的结果,运用第一步归纳假设即可证毕。

例:钓鱼

题目描述

在一条水平路边,有 n ( 2 ≤ n ≤ 25 ) n(2\le n \le 25) n(2n25)个钓鱼湖,从左到右编号为1、2、3、……、n.佳佳有 H ( 1 ≤ H ≤ 16 ) H(1 \le H \le 16) H(1H16)个小时的空余时间,他希望用这些事件钓到尽量多的鱼。他从湖1出发,向右走,有选择的在第 i + 1 i+1 i+1个湖边停留一定的时间钓鱼,最后在某一个湖边结束钓鱼。佳佳测出从第i个湖到第 i + 1 i+1 i+1个湖需要走 5 ∗ T i 5*T_i 5Ti分钟的路,还测出在在第 i i i个湖边停留,第一个5分钟可以钓到鱼 F i F_i Fi,以后再每钓5分钟鱼,鱼量减少 D i D_i Di,为了简化问题,佳佳假定没有其他人钓鱼,也不会有其他因素影响到他钓到期望数量的鱼。请设计算法给出佳佳能钓到最多的鱼的方案。


问题抽取

  • 变量太多了,行走时间,停留时间
  • 湖比较少,我们枚举最后停留的湖,把行走时间变成常量
  • 问题中以5分钟作为基本单位,不妨把5去掉
  • 行走时间为常量,那问题等价为佳佳可以瞬间移动

贪心策略

  • 在第 i ​ i​ i分钟瞬移到可以收获最多鱼的湖去抓鱼,更新这个湖的抓鱼量
  • 第一步正确性显然,若存在最优解包含前 k ​ k​ k步选择,则剩下的时间内也应是剩下时间内的最优选择,故可用第一步构造处使用贪心算法的解与前 k ​ k​ k步成立
  • 证明较显然故略去
  • 最后不要忘记将 n n n中情况合并

复杂度

  • 暴力枚举各时刻可获鱼数最大值: O ( H n 2 ) O(Hn^2) O(Hn2)
  • 堆优化: O ( H n l o g n ) O(Hnlogn) O(Hnlogn)

总结

感觉问题为贪心解法时应注意控制可变量的个数,明确优化对象,方便思考


例:照亮的山景

题目描述

在一片山的上空,高度为 T T T处有 N N N个处于不同水平位置的灯泡。如果山的边界上某一点与灯i的连线不经过山上的其他点,我们称灯i可以照亮该点。开尽量少的灯,使得整个山景都被照亮


优化对象选择

  • 选择电灯?
  • 区间是碎的,难以处理。(图先欠着)

如果向下看

  • 所有的可行方案都照亮了所有的谷底
  • 所有照亮所有谷底的方案都是可行方案(光路是可逆的)
  • 能照亮一个谷底的灯必定是一个连续的区间(图先欠着)

那我们直接以谷底优化对象好了


算法综述

  • 对每一个谷底,预处理能将其照亮的所有灯泡区间
  • 选择最少的灯泡,使得每个区间中至少有一个灯泡被选中了(贪心)

贪心决策(做有智商的莽夫)

  • 显然的决策:如果区间 I I I包含了区间 J J J,那么考虑区间 I I I的决策是无用的,因为必有一点在 J J J中, I I I自然满足
  • 只需讨论所有部分重合和不重合的区间
  • 莽夫:最左侧的区间是最危险的区间,那么就考虑它,选它最右面的点,以保证能照亮更多与它重合的区间,把这些被照亮的区间删去

证明

  • 以步数做归纳,显然莽夫的思考过程就是我们的决策基础
  • 若存在最优决策T包含前k步的决策,若再处理已选灯的区间是多余的,去掉他们,应对后面的区间进行最优决策,而由归纳基础得出存在以贪心决策开头的方案使得其为最优方案
  • 都是一个套路。。。
  • 时间复杂度:直接线性扫描,为 O ( n ) O(n) O(n)

例:潜泳比赛

题目描述

  • 有一支由 N N N人组成的潜水队参与比赛,目标是全队游到对岸。
  • 潜水需氧气筒,整队只有一个氧气筒。
  • 氧气筒可以两人公用,但速度就是两人中较慢者的速度。
  • 任意两人可以一同潜泳。
  • 请设计最优方案,使得最后一名选手到达对岸的时间尽量早。

莽夫思路一

  • 总需要一个人来把氧气筒送回来,而游得再快的人也会被游得慢的人拖累,不妨就让游得最快的人来回送氧气筒

莽夫思路二

  • 如果让最慢的两个人一起过去,在对岸安排好送氧气筒回来的人怎么样?
  • 把最快的两个人先送过去,让他们充当搬运工

请计算

  • 1、 2、 5、 6 、8、 9
  • 1、 2、 2、 2、 2、 2

如果合并这两种思路

  • 这两种思路的共同点,就是把最慢的两个人送过去了,而对于不同的情况有不同的选择
  • 若将所有人按照过河时间升序排序成 a 1 , a 2 , . . . , a N {a_1, a_2, ..., a_N} a1,a2,...,aN,那么易知如果 a 1 + a N − 1 − 2 a 2 < 0 a_1+a_{N-1}-2a_2<0 a1+aN12a2<0时用算法1,反之用算法2,我们就能最快地把最慢的两个人送到对岸去
  • 这样选择研究的对象看起来和谐了一些

证明归纳基础

  • 归纳基础:将最慢的两个人先用最快的方式送过去,不会影响总时间
  • 倘若存在最优策略不是这样:最慢的两个人要么是一起过,要么是拖累着别人过。
  • 这两个人如果是一起过的,那就得有人回来送氧气筒。如果对面有人的话肯定不是他们两个中的一个回来送氧气筒。如果不是 a 1 或 a 2 a_1或a_2 a1a2回来送,把 a 1 , a 2 a_1,a_2 a1,a2调到前面去会更优。那不如把他们的过河顺序都调到前面去
  • 如果这两个人是分开过的,那么还是用 a 1 a_1 a1去送氧气筒最快,那么为什么不把顺序调到前面呢?
  • 完整证明思路与前面基本一样,节省时间略去不提
  • 说不太清楚,有更好的思路请到我的万年不更的博客下留言讨论

时间复杂度

  • 只需线性扫描, O ( n ) O(n) O(n)结束

总结

!找好优化目标!

练:浇花问题

  • 有一长方形花坛,在于其较长边平行的中轴线上安放了若干水龙头,每个水龙头能浇到草地的半径是已知的。
  • 保证存在可以把所有草地都浇到的方案,请把其中一个选择水龙头最少的方案找出来

你可能感兴趣的:(原创)