动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。
通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,具有天然剪枝的功能,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。链接:https://leetcode-cn.com/tag/dynamic-programming/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
其实这个讲的不是非常准确,《算法导论》上面的例题讲解的十分清晰明白还有完整的解题思路。注意重点:
- 动态规划法其实是对递归算法的优化,先有递归解法,再有动态规划解法。如果递归方法都想不出来还想什么动态规划。
- 动态规划往往用来寻找最优解问题,即求最大最小值等等。
所以说,我们求解动态规划时,可以把思路拆分成多个步骤:
- 递归定义最优解的值(把原问题拆分为子问题)
- 求得原问题的朴素递归解法
- 分析朴素递归解法的重叠子问题
- 存储重叠子问题的解法
例题1:算法导论钢条切割:
前两步不同题目思路差别太大不说了。(然而前两步才是最困难的毕竟万事开头难)
分析重叠子问题非常简单,直接脑子想是不行的,这里建议画出递归树分析。(算法导论里面也有)
利用递归树(其实是个图)可以轻松看出问题之间对应关系以及哪个子问题已经解决哪些子问题重复解决。
4.存储重复子问题解法:
最简单粗暴的方法当然是建立一个字典然后问题作为键答案作为值存储,但是这个不常用因为大炮打蚊子。
这题用一个数组进行存储就可以了,事实上很多动态规划问题也都是用数组存储。
5.第四步出来的答案属于“自顶而下”的解决方案,经过逻辑改良可以改成“自底而上”的解决方案。后者优点是代码量小变量较少便于查错与维护。
例题2:https://leetcode-cn.com/problems/regular-expression-matching/
先上朴素递归解法
class Solution { public boolean isMatch(String text, String pattern) { if (pattern.isEmpty()) return text.isEmpty(); boolean first_match = (!text.isEmpty() && (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.')); if (pattern.length() >= 2 && pattern.charAt(1) == '*'){ return (isMatch(text, pattern.substring(2)) || (first_match && isMatch(text.substring(1), pattern))); } else { return first_match && isMatch(text.substring(1), pattern.substring(1)); } } } 作者:LeetCode 链接:https://leetcode-cn.com/problems/regular-expression-matching/solution/zheng-ze-biao-da-shi-pi-pei-by-leetcode/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
对递归过程进行分析,作图(递归树一定要画的,不然纯脑子想保证一会你就晕的脑壳痛):
即知重复运算的次数,若将这些重复运算的结果储存,则每次向下递归消费就从*2变成了+1。
4.存储中间结果:
此题可以使用二维数组存储中间结果,记录string和patten子字符串的开始位置。当然这种有规律的树图也可以使用一维数组进行存储来减少空间复杂度,不过需要在数据读取和索引变换上下点功夫。
也可以自底而上进行求解,这样就可以在使用计算完上层答案之后删除下层空间复杂度更小,也避免频繁扩容数组声明变量请求内存。