动态规划(dynamic programming)是通过组合子问题的解而解决整个问题的。类似于分治策略。他是运筹学的一个分支,是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。
动态规划算法对每个子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。
动态规划算法的设计分为四个步骤:
1、描述最优解的结构。
2、递归定义最优解的值。
3、按自底向上的方式计算最优解的值。
4、由计算出的最优解的值来构造一个最优解。
动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。 其中比较著名的有最短路径问题,背包问题,项目管理,网络流优化等。
来看一下举例:
背包问题 01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等
应用实例 最短路径问题 ,项目管理,网络流优化等
来说一下最优子结构和重叠子问题:
最优子结构:如果一个问题的最优解包含的子问题也是最优的,我们称该问题具有最优子结构的性质,也就是说:总问题包含很多个子问题的解也是最优的。
当一个问题有最优结构时,提示我们动态规划是适用的。
我们寻找最优子结构时,可以遵循下面的共同模式:
1、问题的一个解可以使做一个选择。
2、假设对一个给定的问题,已知的是一个可以导致最优解的选择,可以不关心如何去选择,尽管假定是已知的。
3、在已知这个选择后,要确定哪些子问题会随之发生,以及如何最好地描述所得到的子问题控件。
4、利用一种“剪贴”技术,来证明在问题的一个最优解中,使用的子问题的解本身也必须是最优的。通过假设每一个子问题的解都不是最优的解,然后产生矛盾,可以做到这一点。
我们通过“剪除”非最优的子问题解再“贴上"最优解,就证明了可以得到比原问题的一个更好的解。因此,这和假设已经得到一个最优解相矛盾。 如果我们的问题很多,他们通常类似,所以我们只要对其中一个子问题的”剪贴“处理略加修改,就可以很容易地用于其他子问题。
我们在描述子问题的时候,要尽量保持这个控件简单,然后在需要时再扩充。
在最优子结构中,存在两种问题域的变化方式:
1、有多少个子问题被使用在原问题的一个最优解中。
2、在决定一个最优解中使用那些子问题时有多少个选择。
在动态规划中,算法的运行时间依赖于两个因素的乘积:
1、子问题的总个数。
2、每一个子问题中有多少种选择。
在动态规划中,我们用自底向上的方式来利用最优子结构,也就是说,我们先找到子问题的最优解,解决子问题,然后找到问题的一个最优解。我们寻找问题的一个最优解需要在子问题中做出选择,即选择将用哪一个来求解问题。其代价通常是子问题的代价加上选择本身带来的开销。
重叠子问题:子问题重叠性质是指在用递归算法自顶向下对问题进行求解的时候,每次产生的子问题并不总是新问题,有的子问题也会被重复计算多次。动态规划算法利用了这种行为对每一个问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只要在表格中简单地查看一下结果,从而获得更高的效率。
当一个问题的两个子问题不相互共享,那么他们是相互独立的,对两个子问题来说,如果他们确实是相同的子问题,只是作为不同问题的子问题出现的话,则他们是重叠的。
备忘录方法:
他是动态规划算法的变形,采用自顶向下的递归方式,动态规划采用的是自底向上的方法。 跟动态规划一样,维护了一个记录子问题解的。
备忘录方法为每一个子问题建立一个记录项,初始化时,这个记录项存入一个特殊的值,表示该子问题尚未求解。求解的时候,对每一个待求的子问题先查看他相应的记录项。 记录项中存储的是初始化时存入的特殊值就表示这个子问题是第一次遇到的,就来求出这个子问题的解,并保存在相应的记录项中。用于后续的查看。
如果记录项中存储的是该子问题的解,那么这个时候只要从记录项中取出这个子问题的解答就可以了,不要再去计算了。
2012/10/1
jofranks 于南昌