动态规划总结

来源:互联网 发布:印刷厂计件软件 编辑:程序博客网 时间:2024/05/16 14:37

学习动态规划有一段时间了,总结一下,开始新的算法

一、为什么要使用动态规划?

当回溯效率太低的时候,动态规划是个不错的选择,因为它是不断建立在最优状态上的递推,得出最终结果。

二、动态规划两大标志:

1)最优子结构:一个问题的最优解包含子问题的最优解,这个性质被称为最优子结构

2)重叠子问题:不同问题包含相同的子问题。即:原问题不断分解成同样的子问题,而不是产生新的问题。

重点:重叠子问题和子问题独立性的区别:首先来举个例子:图中有a、b、c、d四个顶点,他们之间存在若干条路径,求从a到d的最短路径,这个可以使用动态规划来实现。但如果求从a到b的最长路径,则不能使用动态规划,因为设存在a--->b--->d,所以原问题分解成a--->b和b--->d的两个子问题。而对于a--->b子问题中可能会包含d节点,这回影响b--->d这个子问题的结果。大家明白了吧,子问题独立性要求是:对于同一个问题的子问题相互独立。这就是与重叠子问题的区别,重叠子问题是相对于不同问题的子问题的。至于最长路径问题好像用到什么NP完全性(难道就是集合上的动态规划?!),暂时还不理解。

三:动态规划解题关键:

通过分析数据,找到状态,然后找到其中的状态转移方程,然后问题解决。

具体实现有两种方法:

1)记忆化搜索:简便、而且思路会很清晰。针对性计算,这是递归的主要好处,从顶到底。

2)递推:无需递归的代价,但可能会计算一些不必要的值,从底到顶。

四:主要题型:

1)DAG模型(即有向无环图),如:最长最短路径问题

2)多阶段决策问题,如:0-1背包问题,最优矩阵,最长公共子序列,最优搜索二叉树

3)集合上的动态规划(状态压缩):如:状态中涉及到“除了一些点之外”类问题,和棋盘和网格中的某些问题(一行一行进行递推)


做题时的一些小技巧:如果要求输出字典序最小的,状态转移最好从前向后进行递推。

位运算的运算级比较低,所以应多用括号。

五:动态规划的效率?

依赖于子问题的个数和每一个子问题中有多少的选择,两者的乘积即为算法的效率。


小小的感悟:永远不要认为你这道题没有做过,做不出来,从题中给出的数据开始,硬是找出两个状态寻找它们之间的联系,用笔画到本上,思路就会出来了。关键是找到状态转移方程。其中用到的一个小技巧,但是一般不会注意到就是:开始可以假设一个状态已知,然后开始分析,向前或者向后都可以。“剪贴”来确保你找到的是最优解,这个证明的时候会用到,但是解题的时候很少用。


OK了,参见:《算法导论》《算法导论入门经典》

六、补充:

1、专题训练动态规划总结:

①动态转移方程中两种形式,d[i][j],一种是必包含第i状态,一种是前j个状态。poj1024中利用前者,优化算法。

②临界状态处理,如果简单处理不可以,则使用if语句判定,结合使用。

③滚动数组中的空间和时间优化。poj1024

④尽量少使用递归。

2、01背包的几种情况

①求最大价值,包不必放满,初始化:memset(d, 0, sizeof(d));

②求最小价值,包不必放满,初始化:

for j = 1...N

d[j] = INF;

d[0] = 0;

③求最大价值,包必须放满,初始化:

for j = 1...N

d[j] = -INF;

d[0] = 0;

3、树形DP

把根的所有子节点看作很多层,选择该子树,或者不选择,于是就可以转变为01背包问题,只是需要在更新前,首先需要对子树更新完成。递归实现即可。

一般,使用d[i]表示以i节点为根节点,并必然包含i节点,会更好处理问题。

原创粉丝点击