You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems,
solving each of those subproblems just once,
and storing their solutions.
为什么分享动态规划
不正经原因:
看看能不能装个逼
大厂面试都会考算法,通过这次分享,争取向各个大厂输送一点多点人才,不管是二进宫还是三进宫
正经原因:
实习的时候,听到一个后端讲:“前端?屁算法不懂!”,当时我看见我的导师破防了,坐在椅子上泣不成声。那一刻我就在想,如果我学会了算法,我一定要赢下所有。
如今,我的算法分享就在眼前,在座的各位必须考虑这会不会是你此生仅有的机会学会动态规划
1. 什么是动态规划?
动态规划是一种数学优化方法。从考试/竞赛/面试的角度看时,通常视作一个算法,常用于解决最优解问题。
上面的英文释义看起来有点像分治算法:都是把大问题拆分成小问题,但是区别在于:
1.1 动态规划的特点
动态规划可解问题满足以下三个特点:
这部分不理解没关系,后面例题中会提到
2. 题目一:斐波那契数列 (leetcode 509)
斐波那契数列既是一道经典的递归入门题目,也可以是一道经典的动态规划入门题目,虽然它不是严格意义上的动态规划
排除通项公式、矩阵快速幂数学解法(因为在座的各位数学水平应该回滚到了初中水平)、排除掉调用内置函数的解法,接下来会提到四种解法:
2.1 递归解法:自顶向下
时间复杂度
O(2^n)
:数一数有多少个蓝色节点即可空间复杂度
O(n)
:栈的深度,这里不考虑尾递归优化重复计算太多,这里重复计算的众多节点,就是上面提到的重复子问题,代码略
2.2 记忆化递归:自顶向下
容易想到,用O(n)的空间存储计算过的结果,代码略
时间复杂度:降到
O(n)
空间复杂度:开辟
O(n)
空间 +O(n)
栈空间 =O(n)
2.3 动态规划:自底向上
既然知道
fib(0) = 0
,fib(1) = 1
,fib(n) = fib(n - 1) + fib(n - 2)
, 从底部向上推导即可时间复杂度:
O(n)
空间复杂度:
O(n)
2.4 动态规划滚动数组优化
因为状态转移方程:
dp[n] = dp[n-1] + dp[n-2]
,在计算下一个状态时,只用到了当前值和前一个值,自然可以用两个变量优化,然后这么滚动下去。时间复杂度:
O(n)
空间复杂度:
O(1)
(不明白没关系,后面有更详细的解释)
2.5 本题小结
fib
的例子可以很好的说明什么是重复子问题。3. 题目二:零钱兑换easy版 (leetcode 322改)
生活中的经验是:能用100的就尽量用100的,否则尽量用50的……依次类推
实际情况表明。这个做法准确无误。这个策略就是贪心策略,不过贪心策略会一直管用吗?
3.1 一点题外话,激活一下大家
写这个部分的时候看到知乎的一条评论
然后检索了一下贪心算法和货币面值的信息:
有远古时期的题目
有用三元人民币打脸的
最后结论是:能满足贪心性质的货币面值组合有不少,1、5、10...这种组合更贴近使用习惯
3.2 真实的第二题
从贪心算法的角度来凑出 15 元:首先取出 1 张 11 元,然后取出 4 张 1 元,共计 5 张
而最优解只需要 5 + 5 + 5,共计三张
因为贪心策略的纲领是:尽量使接下来面对的
w
更小,因此第一步会将w
降到4,而凑出4的代价是很高的,这样就显得鼠目寸光3.3 动态规划求解
题目需要求出:凑出某个金额
w
,需要的最小张数不妨设:
dp[n]
为凑出金额n
所需要的最小张数显然,还可以获得一些初始值:
dp[1] = 1
:凑1元的最优解是一张1元dp[5] = 1
:凑5元的最优解是一张5元dp[11] = 1
:凑11元的最优解是一张11元由于目前只有1,5,11三种面值,所以对于
dp[15]
来说,第一步只有这三种取钱方案,而由于dp定义,这三个里一定有一个是答案dp[15] = dp[4] + 1 = 5
dp[15] = dp[10] + 1 = 3
(最优)dp[15] = dp[14] + 1 = 5
非常容易观察出,
dp[n]
的结果只与dp[n-1]
、dp[n-5]
、dp[n-11]
有关,确切的说:所以实现如下:
3.4 本题小结
贪心算法每个阶段都选择最优解,那么最后自然也是最优解的前提是:每个阶段的选择不影响后面的选择
例如下图中,第一步可以选择H / I, 选择了之后并不影响我后面还能够选择 P / Q,所以步步最优 = 结果最优
而这个图中,第一步选择后,后面的路已经不同了,我能保证第一步最优,但无法保证结果最优,这个图的最优路径肯定要都走一遍才能确定(穷举)
再看这个图,由于Q的入度是2,是一个状态交点,那么如果我知道到Q的最优解,对于后面的过程来说,计算一个最终最优解,根本不关心是走的AHQ,还是AIQ(这也是无后效性的一个体现)
4. 题目三:不同路径(leetcode62)
这道题是许多矩阵型动态规划题目的基础版本,通过此题,我们可以:
4.1 寻常动态规划求解过程
dp
数组的含义:设dp[i][j]
为从左上角走到(i, j)的路径数量dp[i][j]
的解为:dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
(i >= 1 , j >= 1)dp[i][0]
都等于 1,dp[0][j]
都等于 1dp[m-1][n-1]
因此,时间复杂度为
O(mn)
,空间复杂度为O(mn)
的动态规划解如下:4.2 滚动数组优化
滚动数组优化在上文的斐波那契数列中提到过,将
O(n)
的空间优化到了O(1)
,在二维DP问题中,通常可以将O(mn)
的空间复杂度优化到O(min(m, n))
那么为什么存在滚动数组优化:在DP过程中,我们由一个状态转向另一个状态时,很可能之前存储的某些状态信息就已经无用了
比如第一题中的滚动数组优化:
本题的滚动数组优化解释:
解答:
对比:
5. 动态规划与其他算法的对比小结
关于动态规划核心的三要素:
这三要素并不是动态规划专有的,结合贪心算法、递归算法、回溯算法、分治算法来看一下:
The text was updated successfully, but these errors were encountered: