● 每周一言
年龄增长所带来的成熟与经验,不值得夸耀。
导语
贪婪,是一种本性。无后效性的贪婪,称为贪心算法;而全局贪婪,则称为动态规划。那么,贪心和动归的具体思想是什么?分别又是如何实现的?
贪心
贪心算法(Greedy algorithm),是一种在每一步选择中都采取当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。其在有最优子结构问题中尤为有效,最优子结构的意思是局部最优解能决定全局最优解。
举个例子直观感受一下: 超级书架。
题目描述: 给你N个高度分别为Hi的物体,和一个高度为B的书架,并且保证这些物体的高度之和大于等于书架的高度B。问从这N个物体中最少要取出多少个,使得这些取出来的物体所堆起来的总高度不小于书架的高度B。
解法: 此题具备无后效性,是一道典型的贪心算法题。 无后效性是指某个状态以前的过程不会影响以后的状态,只与当前状态有关。因此每次我们只需选取剩余最高的物体,直到高度之和超过书架为止即为答案。
动归
动态规划(dynamic programming)是解决多阶段最优化问题的一种思想方法。动态规划的关键是动态转移方程,很多情况下其编码实现非常简单。基本实现方法可分为如下两步:
递推 由当前状态推导后面的状态,更加强调阶段性。
递归 记忆化搜索,由前面状态得出当前状态,一般承认阶段性。
同样,我们还是通过举例子来直观感受,不妨就举一个与超级书架相似的例子:背包问题。
题目描述: 一个容量为M的背包,现在有N个体积为Ni的物品,如何装包才能让背包容量的利用率最大?
分析: 由于此题不满足“无后效性”,因此如果按照贪心策略装包,很有可能得不到最优解。比如对于容量为10的背包,假设有5个体积依次为(6, 5, 3, 3, 2)的物品,贪心策略会从大到小装包,因此体积为6和3的物品被选出,背包容量利用率为90%。但最优的策略应该是将体积为5、3和2的物品装包,此时利用率能达到100%。此时则不能用贪心算法,而需要用动态规划来解。
解法: 此题是一个典型的简化版 01背包问题,动态转移方程为:f(m) = f(m - k)。其中m为当前计算的容量,0 < m <= M,k为每个物品的体积。外层循环遍历物体体积,内层循环从大到小遍历背包容量。这里多说一句,如果是重复背包问题(每种物品可取多次),内层循环则需改为从小到大遍历背包容量。
典型的动态规划问题还有导弹拦截、字符串距离和最长公共子序列等。敬请期待下节内容 最短路。
结语
感谢各位的耐心阅读,后续文章于每周日奉上,欢迎大家关注小斗公众号 对半独白!