百度百科
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著《Dynamic Programming》,这是该领域的第一本著作。
简单直接的描述,就是指动态规划先解决子问题,再逐步解决大问题。。
与分而治之(part4)、贪婪算法类似,动态规划算法是一种解决问题的方案。
假设你是一名小偷,背着一个可装4磅的背包。
你可以盗窃的东西如下三件,为了让偷到的东西价值最高,你该选择那些商品?
只要商品数量够多,这种算法就行不通。如果用贪心算法,可以找到近似解,但可能并不是最优解。
有木有其它方法找最优解呢?
动态规划先解决子问题,再逐步解决大问题。
每个动态规划都从一个网格开始,背包问题的网格如下:
吉他行
第一个单元格表示背包的容量为1磅。 吉他的重量也是1磅, 这意味着它能装入背包! 因此这个单元格包含吉他, 价值为1500美元。 来看下一个单元格,这个单元格表示背包的容量为2磅, 完全能够装下吉他!这行的其他的单元格也是如此,因为你目前只能把吉他装入背包,其他两种商品还未出现,所以第一行变成下图:
注意:这行表示的是当前的最大价值。
音响行
现在来到第二行,在每一行,可偷的商品都是当前行的商品和之前各行的商品。因此,当前你已经解锁了音响和吉他,但是笔记本电脑还未解锁。现在来看第一个单元格,它表示容量为1磅的背包。 在此之前, 可装入1磅背包的商品的最大价值为1500美元。 背包的容量为1磅, 能装下音响吗? 音响太重了, 装不下! 由于容量1磅的背包装不下音响, 因此最大价值依然是1500美元。 接下来的两个单元格的情况与此相同。
现在来到了第四个单元格,也就是说背包容量为4磅,终于能装下音响,由于音响的价值为3000美元,比1500美元的吉他值钱多了,所以还是偷音响吧。
笔记本电脑行
下面以同样的方式处理笔记本电脑。 笔记本电脑重3磅, 没法将其装入容量为1磅或2磅的背包, 因此前两个单元格的最大价值还是1500美元。
对于容量为3磅的背包, 原来的最大价值为1500美元, 但现在你可选择盗窃价值2000美元的笔记本电脑而不是吉他, 这样新的最大价值将为2000美元!
现在来到这个问题最关键的单元格,对于容量为4磅的背包,当前的最大价值为3000美元, 你可不偷音响, 而偷笔记本电脑, 但它只值2000美元。但是笔记本电脑的重量只有3磅, 背包还有1磅的容量没用! 在1磅的容量中, 可装入的商品的最大价值之前计算过。根据之前计算的最大价值可知, 在1磅的容量中可装入吉他, 价值1500美元。于是有了下面的比较:
于是我们得到了最终的结果:
在这个过程中,我们填入单元格时用到了下面的公式:
①沿着一列往下走时, 最大价值有可能降低吗?
答案: 不可能。 每次迭代时, 你都存储当前的最大价值。 最大价值不可能比以前低!
②行的排列顺序发生变化时结果将如何变化?
答案:没有变化。 也就是说, 各行的排列顺序无关紧要。
③可以逐列而不是逐行填充网格吗?
答案:就这个问题而言, 这没有任何影响, 但对于其他问题, 可能有影响。
④增加一件更小的商品将如何呢?
答案:单元格的按最小商品的重量划分。
⑤可以偷商品的一部分吗?
答案:没法处理。 使用动态规划时, 要么考虑拿走整件商品, 要么考虑不拿, 而没法判断该不该拿走商品的一部分。
但使用贪婪算法可轻松地处理这种情况! 首先, 尽可能多地拿价值最高的商品; 如果拿光了, 再尽可能多地拿价值次高的商品, 以此类推。 例如有以下商品可选:
第一个藜麦最贵,因此要尽量往背包中装藜麦!如果装满,结果就是最佳的;如果藜麦没装满,就接着装入下一个价格最高的商品,以此类推。
⑥动态规划可以处理相互依赖的情况吗?(请参考 4.旅行行程最大化的例子)
答案:没办法建模。 动态规划功能强大, 它能够解决子问题并使用这些答案来解决大问题。 但仅当每个子问题都是离散的, 即不依赖于其他子问题时, 动态规划才管用 。
⑦计算最终的解时会涉及两个以上的子背包吗?
答案:根据动态规划算法的设计, 最多只需合并两个子背包, 即根本不会涉及两个以上的子背包。 不过这些子背包可能又包含子背包。
去伦敦度假,假期两天,但是想要游玩的地方很多,因此列个单子:
对于想去浏览的每个名胜,都列出所需的时间以及你有多想去看看。这里背包问题的约束条件是有限的时间。据此绘制动态规划网格。
答案如下:
假如你还想去巴黎,巴黎景点如下:
从伦敦到巴黎,需要半天0.5天,如果三个地方都去玩,是不是总计4.5天呢?
不是的,因为,去每个地方都得先去巴黎。到达巴黎后,每个地方都只需要1天,总计3.5天即可玩完。
将埃菲尔铁塔 放入背包后,卢浮宫等 将更便宜。此时子问题是相互依赖的。
动态规划功能强大, 它能够解决子问题并使用这些答案来解决大问题。 但仅当每个子问题都是离散的, 即不依赖于其他子问题时, 动态规划才管用 。
举个栗子:
Alex输入了hish,那他原本要输入的是fish还是vista呢?
解决上面问题的网格应该怎么构建,要考虑下面几点:
填充时用什么公式呢?费曼算法(Feynman algorithm)步骤如下:(这怕不是再说废话= =!废慢算法?)
对于背包问题,最终答案总在最后的单元格中;但对于最长公共子串而言,答案是网格中最大的数字——不一定在最后的单元格中。
那么问题的答案出来了,hish和fish的最长公共子串包含三个字母,而hish和vista的最长公共子串包含两个字母。因此Alex很可能原本要输入的是fish。
假设Alex不小心输入了fosh,他原本想输入的是fish还是fort呢?我们使用最长公共子串公式来比较它们:
所以都为2,长度相同,但是fosh和fish更像。那应该找出更合适的区分公式:
比较最长公共子序列:两个单词中都有的序列包含的字母数。
动态规划的实际应用:
Q:请绘制并填充用来计算blue和clues最长公共子串的网格。
A:
《算法图解》第九章:详细的程序思路介绍
此部分学习算法也可参考:https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653190796&idx=1&sn=2bf42e5783f3efd03bfb0ecd3cbbc380&chksm=8c990856bbee8140055c3429f59c8f46dc05be20b859f00fe8168efe1e6a954fdc5cfc7246b0&scene=21#wechat_redirect