动态规划五步曲:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
509. 斐波那契数
动规五部曲:
1、明确dp数组的含义和下标
dp[i]:下标为i(第i+1个数)的斐波那契数值为dp[i]。
2、确定递推公式
dp[i] = dp[i-1] + dp[i-2] (i >=2)
3、初始化dp数组
dp[0] = 0; dp[1] = 1
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
70. 爬楼梯
动规五部曲:
1、明确dp数组的含义和下标 很重要,做题关键!
dp[ i ]:需要爬 i 阶才能到楼顶,这 i 阶有 dp[ i ] 种方式可以爬到楼顶
2、确定递推公式 关键!
dp[i] = dp[i-1] + dp[i-2] (i >=3)
首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶就是dp[i]了。
其次是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶就是dp[i]了。
所以dp[ i ]是 dp[i - 1]与dp[i - 2]之和
tip:在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。
3、初始化dp数组
dp[0] = 0; dp[1] = 1; dp[2] = 2;
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
746. 使用最小花费爬楼梯
和前两题一样,很常规很简单,一刷ac
动规五部曲:
1、明确dp数组的含义和下标
dp[i]:从第i个台阶向上爬到楼顶的最低花费是dp[ i ]。
2、确定递推公式
dp[i] = min ( dp [i +1] + dp [i + 2] )(i >=0)
3、初始化dp数组
dp[dp.length - 1] = cost[cost.length - 1]; dp[dp.length - 2] = cost[cost.length - 2]
4、明确遍历顺序
从后向前
5、打印dp数组(debug验证用)
62.不同路径
方法一:动态规划
动规五部曲:
1、明确dp数组的含义和下标 重要
dp[ i ][ j ]:从最左端( dp[0][0] )到达第i行,第j列( dp[i][j] )格子,总共有dp[ i ][ j ]条路径。
2、确定递推公式 关键
dp[i][j] = dp [i -1][j] + dp [i][j-1] (向右+向下)
3、初始化dp数组 重要
不同于前两题,本题根据题目分析初始化dp数组,需要初始化整个第1行和第1列。
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
方法二:数论
例如m = 3,n = 7
无论怎么移动,都有向下走两步,向右走6步,一共走8步。
所以就转换成了组合问题:C_{m-n+2}^{m-1}。
63. 不同路径 II
和上一题类似,关键点主要在dp数组初始化上。
动规五部曲:
1、明确dp数组的含义和下标
dp[ i ][ j ]:从最左端( dp[0][0] )到达第i行,第j列( dp[i][j] )格子,总共有dp[ i ][ j ]条路径。
2、确定递推公式
如果obstacleGrid[i][j] == 1,则dp[i][j] = 0,否则 dp[i][j] = dp [i -1][j](向右) + dp [i][j-1](向下)
3、初始化dp数组 关键!!
不同于上一题,本题初始化第1行和第1列时,一但出现有障碍(obstacleGrid[i][j] == 1)的情况,该行或该列往后的所有值都为0。(有障碍,无法到达)
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
343. 整数拆分
动规五部曲:
1、明确dp数组的含义和下标
dp[ i ]:数字 i 被拆分后获得的最大乘积是dp[ i ]。
2、确定递推公式 难点及关键点!!
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
dp[i]可以通过两种渠道得到:
1、j * (i - j) 直接相乘。
2、j * dp[i - j],相当于是拆分(i - j),dp[i - j] 可能由多个数组成。
在取最大值的时候,还要比较dp[i]。在递推公式推导的过程中,每次计算dp[i],取最大的而已。
3、初始化dp数组
只初始化dp[1]和dp[2]
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
96.不同的二叉搜索树
动规五部曲:
1、明确dp数组的含义和下标
dp[ i ]:i个不同元素节点组成的二叉搜索树的个数为dp[i] 。
2、确定递推公式 难点及关键点!!
记住吧……
dp[i] += dp[j - 1] * dp[i - j]; j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量。j从1 开始遍历到 i 为止。
dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量
元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量
元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量
元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量
有2个元素的搜索树数量就是dp[2]。
有1个元素的搜索树数量就是dp[1]。
有0个元素的搜索树数量就是dp[0]。
所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]
3、初始化dp数组
dp[0]=1
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)