动态规划是一种常用的算法思想,可以解决一些具有重叠子问题和最优子结构性质的问题。在本文中,我们将介绍三道与动态规划相关的 LeetCode 题目,并提供解题思路和代码实现。
1. 删除并获得点数
题目描述:给定一个整数数组 nums,每次操作中,选择任意一个 nums[i],删除它并获得 nums[i] 的点数。之后,需要删除所有等于 nums[i] - 1 和 nums[i] + 1 的元素。求通过这些操作能获得的最大点数。
解题思路:
- 计算数组中每个数字出现的次数 -> count.get(i) 数字i出现的次数
- dp[i] 表示 选择到i时的最大点数
- 初始化dp[1],dp[1] = count.get(1) ? count.get(1) * 1 : 0;
- 对于 i 两个选择 1. 删除 dp[i] = d[i-2] + i * num[i] 2. 不删除 dp[i] = dp[i-1]
- 遍历 2 ~ maxNum 的数字,分别计算两种选择的路径数,取较大值
var deleteAndEarn = function (nums) {
if (nums.length === 0) return 0; // 空数组返回0
let count = new Map();
let maxNum = 0;
for (let num of nums) {
count.set(num, (count.get(num) || 0) + 1); // 计算每个数字出现的次数
maxNum = Math.max(maxNum, num); // 计算数组中最大的数字
}
let dp = new Array(maxNum + 1).fill(0);
dp[1] = count.get(1) ? count.get(1) * 1 : 0; // 初始化 dp[1]
for (let i = 2; i <= maxNum; i++) {
dp[i] = Math.max(dp[i - 2] + (count.get(i) || 0) * i, dp[i - 1]);
}
return dp[maxNum];
};
2. 不同路径
题目描述:一个机器人位于一个 m x n 网格的左上角,机器人每次只能向下或者向右移动一步。问总共有多少条不同的路径能够到达网格的右下角。
解题思路:
- 初始化 dp 数组,dp[i][j] 表示到达 (i,j) 的路径数
- 第一行和第一列的路径数都为 1(机器人只能向下或者向右移动)
- dp[i][j] = dp[i-1][j] + dp[i][j-1] 表示到达 (i,j) 的路径数等于到达 (i-1,j) 的路径数 + 到达 (i,j-1) 的路径数
- 最后返回 dp[m-1][n-1] 表示到达 (m,n) 的路径数
var uniquePaths = function (m, n) {
let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));
// 初始化第一行和第一列的路径数都为 1
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];
};
3. 最小路径和
题目描述:给定一个包含非负整数的 m x n 网格,找出一条从左上角到右下角的路径,使得路径上的数字总和最小。每次只能向下或者向右移动一步。
解题思路:
- 获取网格的行数和列数
- dp[i][j] 表示到达(i,j) 时路径的最小和
- 初始化第一列和第一行的路径和
- 填充剩余格子 dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
- 返回到达(m,n) 时的路径和
var minPathSum = function (grid) {
let m = grid.length;
let n = grid[0].length;
let dp = new Array(m).fill(0).map(() => new Array(n).fill(0));
dp[0][0] = grid[0][0];
// 初始化第一列和第一行的路径和
for (let i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (let j = 1; j < n; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 计算剩余格子的路径和
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
};
总结:
这三道题目都涉及到动态规划算法,但在具体问题和解题思路上有一些相同点和区别。
相同点:
- 动态规划思想:这三道题目都可以使用动态规划来解决,通过将大问题拆解为小问题并保存中间结果,最终得到最优解。
- 状态定义:在解题过程中,需要定义合适的状态来表示问题的子结构,并根据子结构之间的关系进行状态转移。
区别:
- 问题描述:这三道题目涉及到不同的问题领域。"删除并获得点数"是一个涉及数组操作的问题,"不同路径"是一个二维网格的路径计数问题,而"最小路径和"是一个求解网格路径最小和的问题。
- 解题思路:尽管都是动态规划题目,但每道题目的解题思路和状态转移方程有所不同。例如,"删除并获得点数"需要考虑选择删除和不删除的情况,"不同路径"涉及到累加路径数,而"最小路径和"需要选择路径和最小的方向进行移动。
- 数据结构:虽然都是使用动态规划,但问题的特点导致在实现过程中可能会使用不同的数据结构,如数组、矩阵等。