一切计算机问题,解决方法可以归结为两类:分治和封装。分治是减层,封装是加层。
动态规划问题同样可以用这种思路,分治。
它可以划分为多个子问题解决,那这样是不是用简单的递归就完成了?也许是的,但是这样会涉及太多的不便的操作。因为子问题有重叠!
针对这种子问题有重叠的情况的解决,就是提高效率的关键。
所以动态规划问题可以总结为:最优子结构和重叠子问题。
解决这个子问题的方式的关键就是:memoization,备忘录。
动态规划算法分以下4个步骤:
- 描述最优解的结构
- 递归定义最优解的值
- 按自底向上的方式计算最优解的值 //此3步构成动态规划解的基础。
- 由计算出的结果构造一个最优解。 //此步如果只要求计算最优解的值时,可省略。
现在举一个具体例子来看:LCS
LCS问题即最长公共子序列问题,它不要求所求得的字符在所给的字符串中是连续的(例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子序列,则输出它们的长度4,并打印任意一个子序列)。
动态规划思想:BDCABA和ABCBDAB的LCS等于BDCAB和ABCBDAB的LCS,和BDCABA和ABCBDA的LCS中大的值。
设计个函数子串一为X(n),子串二为Y(m),他们俩的LCS为L(X(n),Y(m))。将上面的文字转为函数:
L(X(n),Y(m))=max(L(X(n-1),Y(m)),L(X(n),Y(m-1)));
看出子问题的结构了吧,下图更简单:
看到第一行和第一列都是0了吗?思考一下为什么
再看一个题:Edit Distance:
Given two words word1 and word2, find the
minimum number of steps required to
convert word1 to word2. (each operation is
counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character
a.首先是有两个字符串,这里写一个简单的 abc和abe
b.将字符串想象成下面的结构。
A处 是一个标记,为了方便讲解,不是这个表的内容。
abc | a | b | c | |
abe | 0 | 1 | 2 | 3 |
a | 1 | A处 | ||
b | 2 | |||
e | 3 |
c.来计算A处 出得值
它的值取决于:左边的1、上边的1、左上角的0.
按照Levenshtein distance的意思:
上面的值和左面的值都要求加1,这样得到1+1=2。
A处 由于是两个a相同,左上角的值加0.这样得到0+0=0。
这是后有三个值,左边的计算后为2,上边的计算后为2,左上角的计算为0,所以A处 取他们里面最小的0.
一次类推便可以得到memoization
想想为什么初始化第一列和第一行由0递增?答案在代码中:
https://github.com/kcrosswind/leetcode/blob/master/Solution_minDistance.java
部分位子摘自:http://wdhdmx.iteye.com/blog/1343856
http://blog.csdn.net/v_JULY_v/article/details/6110269
由LCS到编辑距离—动态规划入门—算法学习笔记,码迷,mamicode.com