(作者:finallyly 出处:博客园)
最近面试笔试,碰到最多的问题就是DP型问题。但是遗憾的是,没有能够完全给出正确的代码。一般是想到一半就卡住了,然后转向其他一些方法解答问题。
更够用动态规划思路解决的问题一般具有两个特点:子问题交叠,子结构最优。
第一个特点是说,subproblem的解决要依赖于多个subsubproblem的解决。所以为了避免重复计算,DP算法一般会维护一个table.也就是以空间换时间。这种思想在很多地方都有应用。比如bayes推理网络里面的变量消除算法,就有这种思路的影子。
第二个特点optimal structure 是说问题的最优解有该问题划分出来的子问题的最优解揉捏而成。
动态规划思路和divide-and-conquer思路的区别:
个人认为动态规划思路容纳divide-and-conquer思路作为其本身的一部分,都是自上而下分析,自下而上求解。不同之处在于DP方法一般会容纳一个table.
动态规划思路的解题难点:
“交叠”这个性质,一般都可以用肉眼观察出来,最关键的步骤是如何准确地定义”子问题,如何能够有效利用子问题的optimal solution,从而避免把整个table变成一个所有可能结果的枚举空间。
几个动态规划的例子
(1)求数组最长递增子序列;
这道题《编程之美》里面有。这本书确实不错,讲解的非常到位,但是遗憾的是本书中关于这道题的讲解不够深入,他给出的算法一,如果题目仅仅要求最长子序列的长度的话,那么这个思路可以。但是如果题目要求求数组的最长递增子序列的话,则要非常遗憾了。
网上有关于这道题的划归为LCS的解法,即求原序列a和其递增排序后的b的longest common sequence.个人认为只是最直接的方法。
如果不划归为LCS。那么问题就复杂了因为 substr(a)的最长递增子序列未必是a的最长递增子序列。如a=3451的LIS为345,而3451234的LIS为1234.之所以会出现这种冲突是因为出现了“新的后起之秀”从而产生了新的、可能与原来的LIS相抗衡的LIS。对于这些新的后起之秀a[i],又可以分为两类。一类是成为新的开始位置的数据;一类是a[i]<a[i-1]&&a[i]>=a[k] from 0<=k <i.下面呢,我们再来设计程序中的存储结构。
typedef struct Node
{
int maxmum;//当前最大值
int candidate;//比当前最大值小但是有竞争力的值
int maxpath;//最大值的对应的路径的前一个结点下标
int candidatepath;//后续值对应的前一个结点下标
}Node
map<pair<pair<int,int>,node>> mymap
如a=[3,4,8,5,6,1,2,3,4];
(2)两个字符串的最长公共子串