图片来源:各大CSDN博主博客,视频,以及程序员小灰(微信公众号)
动态规划(dynamic programming)思想
朴素递归算法之所以效率很低,是因为它反复求解相同的子问题。因此,动态规划方法仔细安排求解顺序,对每个子问题只求解一次,并将结果保存下来。如果随后再次需要此子问题的解,只需查找保存的结果,而不必重新计算。因此,动态规划方法是付出额外的内存空间来节省计算时间,是典型的时空权衡的例子。而时间上的节省可能是非常巨大的:可能将一个指数时间的解转化为一个多项式时间的解。如果子问题的数量是输入规模的多项式函数,而我们可以在多项式时间内求解出每个子问题,那么动态规划方法的总运行时间就是多项式阶的。
*******动态规划一定要找递推公式!!!*******
https://www.zhihu.com/question/23995189
对于DP的各种理解:
动态规划的核心是定义状态和状态转移方程(注意,关键在于定义方程而非关注递推式的求解方法)
动态规划就是 使用递归的思路 找到正确的算法步骤使得得到正确的解,然后使用迭代的方式写出来。(如此便验证了那句自顶向下的考虑问题,自底向上的解决问题)
上图中最右边流程即为递归,左边为迭代(递归的思考,迭代的解决)
动规中三个重要概念: 状态转移方程 边界 最优子结构
动态规划走台阶:
有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。
比如,每次走1级台阶,一共走10步,这是其中一种走法。我们可以简写成 1,1,1,1,1,1,1,1,1,1。
再比如,每次走2级台阶,一共走5步,这是另一种走法。我们可以简写成 2,2,2,2,2。
我们想走到10,如果一次只能走一步或走两步。那么,不考虑之前的台阶。想走到10只有从9走一步或从8走两步。(这便是重构解,把大问题划分为小问题。)
此时我们假设 从0-9 一共有X种走法 那么此时的从0-10 也是X中走法
而0-8一共有Y种走法 那么在所有情况上再加两步到10 还是Y中走法
所以 最后走到10步一共有X + Y种走法
那么 从这一步我们便可以得出一个结论。
0-10级台阶的走法数量 = 0-9级台阶的走法数量 + 0-8级台阶的走法数量
F(10) = F(9) + F(8)(F9 F8 分别是10的最优子结构)
同理 F(9) = F(8) + F(7) F(8) = F(7) + F(6)
而且当台阶有两级的时候有两种走法 当台阶为1时只有1种走法
由此可以归纳出
F(1) = 1
F(2) = 2 // 1 和 2 是边界
F(n) = F(n - 1) + F(n- 2)(n >= 3) //状态转移方程
//以上便是问题建模 接下来是求解部分
若 简单的使用递归求解,则会造成大量重复计算问题。
由此可以看出大量重复计算
那么 ,如何解决? 可以把结果都放在一个数组里,这个数组被称为备忘录,也可以如下:使用一个哈希。
基于哈希的递归:
如此 时间空间复杂度便都是o(n)
但这还不是真正的动态规划实现。时间复杂度已经不能再小了,那么能不能把空间复杂度进一步缩小?
这便验证了DP的又一个特点:自顶向下的分析问题,自底向上的求解问题。
那么 :
如此变成了o(1)的空间复杂度