简单 LV 问题轻松 AC 👏
中等 LV 问题思考 AC 👏
困难 LV 问题 看懂答案思路 🙉
Flag 立的我害怕 🌝🌝
回去总结实践一下,可以达到
引用:
动态规划是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题
换而言之,通过历史记录来减少重复计算,而历史记录一般用一维数组 or 二维数组,甚至三维数组进行存储
简单 LeetCode No.70
只讨论 DP 解法
斐波那契数列
问题是要求爬 n 级的台阶总共由多少种爬法?
那我定义爬上 i 级台阶 共有 DP[i] 种爬法,
当 i = n 时,DP[n] 是我们想要的值
再想,分解子问题,什么是 DP[n] 的子问题?
DP[n-1] DP[n-2]... 是 DP[n] 的子问题
以此类推
那我们需要找到 DP[n] 与它的子问题的关系
问: 它们具有什么关系?
跳到 第 n 台阶的情况
因为每次只能跳一个台阶,或两个台阶
得出 状态转移方程
所以我们要给出 n = 1,n=2 的初始值
万事俱备,写出代码
n=0 为特判
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
if (n === 0) {
return 0
}
let deps = [];
deps[1] = 1;
deps[2] = 2;
for(let i = 3; i <= n; i++) {
deps[i] = deps[i-1] + deps[i-2];
}
return deps[n];
};
时间复杂度,空间复杂度
还可以优化一下么?
中等 LeetCode No.62
只讨论 DP 解法
杨辉三角
当机器人从左上角走到(i, j) 这个位置时,一共有 DP[i] [j] 种路径
走到 S[i][j] 位置的情况:
DP[0][x], DP[x][0] 为我们的初始值
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function(m, n) {
if (m <= 0 || n <= 0) {
return 0
}
let DP = Array(m).fill(Array(n))
for(let i = 0; i < m; i++) {
DP[i][0] = 1
}
for(let j = 0; j < n; j++) {
DP[0][j] = 1
}
for(let i = 1; i < m; i++){
for(let j = 1; j < n; j++) {
DP[i][j] = DP[i-1][j] + DP[i][j-1]
}
}
return DP[m-1][n-1]
};
时间复杂度
空间复杂度
问题来了,怎么优化?
困难 LeetCode No.72
目标是求解长度为 i 的 word1,转化成 j 长度的 word2, 所使用的最少操作次数为 DP[i] [j]。
当 word1[i] 与 word2[j] 相等,我们不需要改操作
当 word1[i] 与 word2[j] 不相等,我们需要进行,增加,删除,替换三种操作
DP[i][j] = DP[i - 1][j - 1] + 1 替换操作
DP[i][j] = DP[i][j - 1] + 1 插入操作
DP[i][j] = DP[i - 1][j] + 1 删除操作
Why?🙄
i \ j | '' | R | 0 | S |
---|---|---|---|---|
'' | 0 | 1 | 2 | 3 |
R | 1 | |||
O | 2 | |||
S | 3 | |||
E | 4 |
空间压缩
通过 DP Table 发现
DP[i][j] 代表 s[i][j] 是否是回文字符串
虽然遇到难的还是不会,但是我们已经找到了基本的套路。🌝
✅ 动态规划 ❌ 贪心 ❌ 回溯 ❌ 分支界定 ❌ 分治
定义 DP 数组含义 难点
目的储存子问题结果
思考三部曲
编码三部曲
处理特判