您的位置:首页 > 职场人生

动态规划

2015-09-04 23:57 375 查看
做完贪心就该动归了。

动归的基本想法就是保存之前计算的结果为下一步做准备。这个计算结果要包括之前的各种可能。解题的关键在于找到递归的方程/关系,只要找到这个则写出程序并不困难。时间上不太可能优化,除非特例;空间上的优化则是看是不是能把结果矩阵转换成一个一维数组来实现。

题目和解法归纳:

1.
Triangle

这个题目可以从上到下做,但这样做就成了深度遍历,需要回溯,时间复杂度高,code也复杂。但如果反过来从下而上做的话,只需要每次选出当前第n层min(An[i], An[i+1])并且加到上一层的An-1[i]上,这样得到的上一层的An-1数组即是当前最优的最优的从第n-1层到第n层的结果。继续计算到顶即可。

2.
Minimum Path Sum

因为这个题目中到达最终点的路径有很多条,所以要找到那个最短的必须计算从起点到所有点距离才能得到最短那个。题目限制所有路径必须向右向下,所以第一行和第一列的结果其实已经是确定的了。然后从A[1][1]开始所有的点的最短路径长度都是由上边和左边的的最短路径长度的最小值加上这个点的值决定的。时间复杂度O(m*n),空间复杂度O(m*n)。继续优化空间复杂度的话可以做到O(n)。

 

3.
Maximal Rectangle (这道题反复优化了很久才搞定)

简单思路当然是遍历所有可能的长方形,计算全是1的长方形的面积,去最大。这个对m*n矩阵就是O(m^2 * n^2)的时间复杂度,不靠谱。

于是思路变成怎么能够只计算有效的长方形。这样一来,当列遍历从上到下的时候,行遍历就需要从右到左,这样才能保证计算过的地方不需要再计算一次,于是复杂度变成了O(m^2 * n),也就是一列只需要挨个看一次就够了。

但这样有些行仍需要被重新计算。为了只做必要的计算,不得已做了两件事:一个是cache数组,存放在当前列的情况下,这个行从这里开始向右直到遇到0,一共有多少个1;另一个是栈,存放比当前位置cache值小的那些行下标和预期宽度。于是当遍历每个列的时候,见到当前行的cache比上一行大,当前行下标和宽度入栈,继续下一轮循环;如果当前行cache比上一行小,则循环从栈中取出宽度大于当前cache的pair,计算面积,直到栈空或者栈顶pair的宽度小于当前cache;这是如果是栈空,则将最后栈顶那个位置和当前的宽度入栈以备今后取出计算(现在计算为时过早,结果不一定是有意义的),如果栈不空,则计算当前宽度到最后一个比它宽的行之间的面积。

循环至所有行列遍历一遍。时间复杂度O(m*n),空间复杂度O(m)。

4.
Edit Distance

被一些书奉为动归经典题目。此题关键在于想清楚插入删除替换最少需要的次数和位置无关,后面的就是典型的大矩阵记录每一步然后回溯的时候不需要去递归而是查表了。

5.
Distinct Subsequences

这个题目关键点是想清楚如果T在最后增加一个字母,如果这个字母和S的最后一个不一样,则结果等同于增加前的结果;如果一样,则结果等同于T不增加这个字母和S去掉最后一个字母后的结果加上T增加这个字母和S去掉最后字母的结果。于是变成了列大表。又因为如果S长度小于T则一定没戏,所以大表只有右上角是有货的,可以减少一半计算量。当然,大表可以变成数组,节省空间。

6.
Scramble String

对于每一步,用result[m][i][j]表示从s1[i]和s2[j]开始,长度为m的两个字符串是不是满足条件。于是对于每一个k,0<k<n,有result[m][i][j]=(result[k][i][j] && result[m - k][i + k][j + k]) || (result[k][i][j + m - k] && result[m - k][i + k][j])。也就是把两个待比较的字串在k的地方分成两截,分别看看第一段和对方两个段是不是满足条件的,只要有一个组合满足条件就算搞定这轮了。

7.
Interleaving String

类似其他题目,这个题目关键在于找到递归的规律:s1的前i个字符跟s2的前j个字符跟s3的前i+j个字符是不是满足条件,取决于s1的第i个字符或者s2的第j个字符是不是跟s3的第i+j个字符相等,以及在这个匹配的字符之前的其他字符是不是满足interleaving的条件,如果相等且满足条件,则这一步结果为true。最终输出矩阵最右下角的结果。

8. Palindrome Partitioning
此题关键在于构建一个bool类型的二维数组isPal

。此处n是字符串s的长度,isPal[i][j]表示字符串的第i个字符到第j个字符是Palindome的。这个二维数组的结构特点是:0)i>j的所有地方一定是false;1)主对角线一定是true,因为每个字符自己是Palindome的;2)如果i和j是相邻的,那么只有他们俩相等了才是Palindome;3)如果i和j不相邻,则当i和j两处字符相同并且中间是Palindome的时候isPal[i][j]才是true。得到这个数组后,问题变成了在isPal矩阵中从左上角找到一条到达右侧边界的所有路线问题:当isPal[i][j]==true的时候,去看isPal[j][...]行找值为true的cell,以此类推至找到边界。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息