322. 零钱兑换

var coinChange = function(coins, amount) {const dp = new Array(amount + 1).fill(Infinity); // 多一项dp[0] = 0; // 凑出0元,不需要硬币for (let i = 1; i <= amount; i++) { // 从 1 元开始凑for (let coin of coins) {if (i - coin < 0) { // 不能选这个硬币continue;}dp[i] = Math.min(dp[i], dp[i - coin] + 1); // 这样算最小的技巧}}return dp[amount] !== Infinity ? dp[amount] : -1;};
300. 最长递增子序列

缩小问题范围。
var lengthOfLIS = function(nums) {let n = nums.length;if (n <= 1) {return n;}let dp = new Array(n).fill(1);for (let i = 1; i < n; i++) {// 前 i - 1 个数里,比 nums[i] 小的数对应的 dp 最大的数for (let j = 0; j <= i - 1; j++) {if (nums[j] < nums[i]) {dp[i] = Math.max(dp[i], dp[j]) + 1;}}}return Math.max(...dp);};
279. 完全平方数

状态转移方程的寻找,之前局限于 i 到 i - 1,通过这道题我们知道还可以跳跃: i 到 j ( 1 <= j <= i * i)
var numSquares = function(n) {const dp = (new Array(n + 1)).fill(0); // 初始值边界应该多少?自己应该去举例for (let i = 1; i <= n; i++) { // 下标的取法,也要去试验let min = Infinity;for (let j = 1; j * j <= i; j++) { // dp 的上一步需要取最小才行if (min > dp[i - j * j]) {min = dp[i - j * j];}}dp[i] = min + 1;}return dp[n];};
96. 不同的二叉搜索树
var numTrees = function(n) {let dp = new Array(n + 1).fill(0);dp[0] = 1;dp[1] = 1;for (let i = 2; i <= n; i++) {for (let j = 0; j < i; j++) {dp[i] += dp[j] * dp[i - 1 - j]; // 就是硬找规律}}return dp[n];};
矩阵 和 路径
64. 最小路径和
以相邻的块 决定 下一个 dp 的值
var minPathSum = function(grid) {let rows = grid.length;let cols = grid[0].length;let dp = new Array(rows).fill(-1).map(() => new Array(rows).fill(-1)); // 题目要求 非负for (let row = rows - 1; row >= 0; row--) {for (let col = cols - 1; col >= 0; col--) {let bottom = (row + 1 < rows) ? dp[row + 1][col] : Infinity;let right = (col + 1 < cols) ? dp[row][col + 1] : Infinity;let min = Math.min(bottom, right);dp[row][col] = (min === Infinity ? 0 : min) + grid[row][col];}}return dp[0][0];};
但是,也有 dfs 思路:
岛屿问题中经常使用的思路。
var minPathSum = function(grid) {let rows = grid.length;let cols = grid[0].length;let ans = [];function dfs(path, row, col) {if (row === rows - 1 && col === cols - 1) {let sum = path.reduce((pre, cur) => pre + cur, 0) + grid[row][col];ans.push(sum);return;}if (!(0 <= row && row < rows && 0 <= col && col < cols)) {return;}// 回溯的那感觉啊dfs([...path, grid[row][col]], row, col + 1);dfs([...path, grid[row][col]], row + 1, col);}dfs([], 0, 0);return Math.min(...ans);};
62. 不同路径
var uniquePaths = function(m, n) {if (m === 1 || n === 1) {return 1;}let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));for (let row = m - 1; row >= 0; row--) {for (let col = n - 1; col >= 0; col--) {if (row === (m - 1) || col === (n - 1)) {dp[row][col] = 1;continue;}dp[row][col] = dp[row + 1][col] + dp[row][col + 1];}}return dp[0][0];};
63. 不同路径 II
和 62 差不多的解法。
var uniquePathsWithObstacles = function(obstacleGrid) {let m = obstacleGrid.length;let n = obstacleGrid[0].length;if (m === 1 || n === 1) {let tmp = obstacleGrid.flat();if (tmp.includes(1)) {return 0;}return 1;}let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));for (let row = m - 1; row >= 0; row--) {for (let col = n - 1; col >= 0; col--) {if (obstacleGrid[row][col] === 1) {dp[row][col] = 0continue;}if ((row === m - 1) && (col === n - 1)) {dp[row][col] = 1;continue;}if (row + 1 >= m) {dp[row][col] = dp[row][col + 1];} else if (col + 1 >= n) {dp[row][col] = dp[row + 1][col];} else {dp[row][col] = dp[row + 1][col] + dp[row][col + 1];}}}return dp[0][0];};
45. 跳跃游戏 II
var jump = function(nums) {let n = nums.length;let dp = new Array(n).fill(Infinity);dp[n - 1] = 0; // 边界条件写明白for (let i = n - 2; i >= 0; i--) {let cur = nums[i];if (cur === 0) {dp[i] = Infinity;} else {let min = Infinity;for (let j = 1; (j <= cur && j < n); j++) { // 递归算步伐if (dp[i + j] === Infinity) {continue;}if (min > dp[i + j]) { // 取最小min = dp[i + j];}}dp[i] = min + 1;}}return dp[0];};
32. 最长有效括号
做过最难的动态规划,但也是典型的动态规划,并不超纲。
var longestValidParentheses = function(s) {let n = s.length;if (n <= 1) {return 0;}if (n === 2) {return s === '()' ? 2 : 0;}let dp = new Array(n).fill(0);for (let i = 0; i < n; i++) {let cur = s[i];if (cur === '(') {dp[i] = 0;} else {if (s[i - 1] === '(') {dp[i] = 2 + ((i - 2 >= 0) ? dp[i - 2] : 0);} else {if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] === '(') {dp[i] = 2 + dp[i - 1] + ((i - dp[i - 1] - 2 >= 0) ? dp[i - dp[i - 1] - 2] : 0);}}}}return Math.max(...dp);};
221. 最大正方形
规律不是特别好理解:dp[i][j] 表示 以 matrix[i][j] 为 右下顶点 的正方形的最大边长。
var maximalSquare = function(matrix) { // 这动态规划好厉害let rows = matrix.length;let cols = matrix[0].length;const dp = new Array(rows).fill(0).map(() => new Array(cols).fill(0));let max = 0;for (let row = 0; row < rows; row++) {for (let col = 0; col < cols; col++) {if (matrix[row][col] === '1') { // 更简洁的写法:只有为1才会被操作if (row === 0 || col === 0) { // 边上元素单独标记dp[row][col] = matrix[row][col] === '1' ? 1: 0;} else {dp[row][col] = Math.min(dp[row - 1][col - 1], dp[row - 1][col], dp[row][col - 1]) + 1;}if (max < dp[row][col]) {max = dp[row][col];}}}}return max * max;};
91. 解码方法
var numDecodings = function(s) { // 动态规划,没有要求具体的例子,只是要数目let n = s.length;if (n === 0) {return 0;}let dp = new Array(n + 1).fill(0);dp[0] = 1;for (let i = 1; i < n + 1; i++) {if (s[i - 1] !== '0') {dp[i] = dp[i - 1];}let cur = s.slice(i - 2, i);if (Number(cur) <= 26 && (s[i - 2] !== '0') && i >= 2) {dp[i] += dp[i - 2]}}return dp[n];};
上台阶问题
62. 不同路径
var uniquePaths = function(m, n) {if (m === 1 || n === 1) {return 1;}let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));for (let row = m - 1; row >= 0; row--) {for (let col = n - 1; col >= 0; col--) {if (row === (m - 1) || col === (n - 1)) {dp[row][col] = 1;continue;}dp[row][col] = dp[row + 1][col] + dp[row][col + 1];}}return dp[0][0];};
63. 不同路径 II
稍微比62考虑情况多一点
var uniquePathsWithObstacles = function(obstacleGrid) {let m = obstacleGrid.length;let n = obstacleGrid[0].length;if (m === 1 || n === 1) {let tmp = obstacleGrid.flat();if (tmp.includes(1)) {return 0;}return 1;}let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));for (let row = m - 1; row >= 0; row--) {for (let col = n - 1; col >= 0; col--) {if (obstacleGrid[row][col] === 1) {dp[row][col] = 0continue;}if ((row === m - 1) && (col === n - 1)) {dp[row][col] = 1;continue;}if (row + 1 >= m) {dp[row][col] = dp[row][col + 1];} else if (col + 1 >= n) {dp[row][col] = dp[row + 1][col];} else {dp[row][col] = dp[row + 1][col] + dp[row][col + 1];}}}return dp[0][0];};
45. 跳跃游戏 II
var jump = function(nums) {let n = nums.length;let dp = new Array(n).fill(Infinity);dp[n - 1] = 0; // 边界条件写明白for (let i = n - 2; i >= 0; i--) {let cur = nums[i];if (cur === 0) {dp[i] = Infinity;} else {let min = Infinity;for (let j = 1; (j <= cur && j < n); j++) { // 递归算步伐if (dp[i + j] === Infinity) {continue;}if (min > dp[i + j]) { // 取最小min = dp[i + j];}}dp[i] = min + 1;}}return dp[0];};
