目录
①力扣62. 不同路径
难度 中等
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7 输出:28
示例 2:
输入:m = 3, n = 2 输出:3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。 1. 向右 -> 向下 -> 向下 2. 向下 -> 向下 -> 向右 3. 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3 输出:28
示例 4:
输入:m = 3, n = 3 输出:6
提示:
1 <= m, n <= 100
- 题目数据保证答案小于等于
2 * 10^9
class Solution {
public:
int uniquePaths(int m, int n) {
}
};
解析代码
dp[i][j] 表示:到 [i, j] 位置处,⼀共有多少种方式。状态转移方程为dp[i][j] = dp[i-1][j] + dp[i][j-1];
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); // 多开一行一列
dp[0][1] = 1; // 虚拟结点,保证后面填表正确(让第一行第一列都是1)
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m][n];
}
};
②力扣63. 不同路径 II
难度 中等
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
示例 1:
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2
条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2:
输入:obstacleGrid = [[0,1],[0,0]] 输出:1
提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j]
为0
或1
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
}
};
解析代码
本题为力扣62不同路径的变型,只不过有些地方有障碍物,只要在状态转移上稍加修改就可解决。
- dp[i][j] 表示:到 [i, j] 位置处,一共有多少种方式。
状态转移方程:
- 如果不是障碍物:dp[i][j] = dp[i-1][j] + dp[i][j-1];
- 如果是障碍物:dp[i][j] = 0;
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size(), n = obstacleGrid[0].size();
// dp[i][j] 表示:到 [i, j] 位置处,⼀共有多少种方式。
vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); // 多开一行一列
dp[0][1] = 1; // 虚拟结点,保证后面填表正确(让第一行第一列都是1)
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(obstacleGrid[i-1][j-1] == 0) // 如果不是障碍物
dp[i][j] = dp[i-1][j] + dp[i][j-1];
// 如果是障碍物,dp[i][j]是0,本来就初始成0,不用处理
}
}
return dp[m][n];
}
};
③力扣LCR 166. 珠宝的最高价值
难度 中等
现有一个记作二维矩阵 frame
的珠宝架,其中 frame[i][j]
为该位置珠宝的价值。拿取珠宝的规则为:
- 只能从架子的左上角开始拿珠宝
- 每次可以移动到右侧或下侧的相邻位置
- 到达珠宝架子的右下角时,停止拿取
注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]
。
示例 1:
输入: frame = [[1,3,1],[1,5,1],[4,2,1]]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最高价值的珠宝
提示:
0 < frame.length <= 200
0 < frame[0].length <= 200
class Solution {
public:
int jewelleryValue(vector<vector<int>>& frame) {
}
};
解析代码
dp[i][j] 表示:到 [i, j] 位置处,此时的最大价值。
状态转移方程为dp[i][j] = frame[i-1][j-1] + max(dp[i-1][j], dp[i][j-1]);
class Solution {
public:
int jewelleryValue(vector<vector<int>>& frame) {
int m = frame.size(), n = frame[0].size();
// dp[i][j] 表示:到 [i, j] 位置处,此时的最大价值
vector<vector<int>> dp(m+1, vector<int>(n+1, 0)); // 多开一行一列
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = frame[i-1][j-1] + max(dp[i-1][j], dp[i][j-1]);
}
}
return dp[m][n];
}
};
④力扣931. 下降路径最小和
难度 中等
给你一个 n x n
的 方形 整数数组 matrix
,请你找出并返回通过 matrix
的下降路径 的 最小和 。
下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col)
的下一个元素应当是 (row + 1, col - 1)
、(row + 1, col)
或者 (row + 1, col + 1)
。
示例 1:
输入:matrix = [[2,1,3],[6,5,4],[7,8,9]] 输出:13 解释:如图所示,为和最小的两条下降路径
示例 2:
输入:matrix = [[-19,57],[-40,-5]] 输出:-59 解释:如图所示,为和最小的下降路径
提示:
n == matrix.length == matrix[i].length
1 <= n <= 100
-100 <= matrix[i][j] <= 100
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& matrix) {
}
};
解析代码
dp[i][j] 表示:到 [i, j] 位置处,此时下降路径最小和。
状态转移方程是dp[i][j] = matrix[i-1][j-1] + min(min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]);
要注意初始化:
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& matrix) {
int n = matrix.size();
// dp[i][j] 表示:到 [i, j] 位置处,此时下降路径最小和
// 初始化,保证填表正确,初始化成INT_MAX是为了不影响第1列和最后1列的比较大小
vector<vector<int>> dp(n+1, vector<int>(n+2, INT_MAX)); // 多开一行两列
for(int i = 0; i < n+2; ++i) // 这里初始化成0是为了不影响第一行的值
{
dp[0][i] = 0;
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = matrix[i-1][j-1] + min(min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]);
}
}
int ret = INT_MAX;
for(int i = 1; i <= n; ++i)
{
ret = min(dp[n][i], ret);
}
return ret;
}
};
⑤力扣64. 最小路径和
难度 中等
给定一个包含非负整数的 m x n
网格 grid
,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 输出:7 解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入:grid = [[1,2,3],[4,5,6]] 输出:12
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 200
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
}
};
解析代码
和之前的题类似,只是初始化要改一改。
dp[i][j] 表示:到 [i, j] 位置处,此时的最小路径和。
状态转移方程:dp[i][j] = grid[i-1][j-1] + min(dp[i-1][j], dp[i][j-1]);
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
// dp[i][j] 表示:到 [i, j] 位置处,此时的最小路径和
// 初始化,保证填表正确,初始化成INT_MAX是为了不影响第1列和最后1列的比较大小
vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX)); // 多开一行一列
dp[0][1] = dp[1][0] = 0; // 初始化保证填表正确,原来填了INT_MAX
for(int i = 1; i <= m; ++i)
{
for(int j = 1; j <= n; ++j)
{
dp[i][j] = grid[i-1][j-1] + min(dp[i-1][j], dp[i][j-1]);
}
}
return dp[m][n];
}
};
⑥力扣174. 地下城游戏
难度 困难
恶魔们抓住了公主并将她关在了地下城 dungeon
的 右下角 。地下城是由 m x n
个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快解救公主,骑士决定每次只 向右 或 向下 移动一步。
返回确保骑士能够拯救到公主所需的最低初始健康点数。
注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
示例 1:
输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]] 输出:7 解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7 。
示例 2:
输入:dungeon = [[0]] 输出:1
提示:
m == dungeon.length
n == dungeon[i].length
1 <= m, n <= 200
-1000 <= dungeon[i][j] <= 1000
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
}
};
解析代码
路径dp问题,这道题的状态表示如果定义成:从起点开始,到达 [i, j] 位置的时候,所需的最低初始健康点数。 那么分析状态转移方程的时候会有一个问题:那就是当前的健康点数还会受到后面的路径的影响。也就是从上往下的状态转移不能很好地解决问题。
这个时候我们要换⼀种状态表示:从 [i, j] 位置出发,到达终点时所需要的最低初始健康点 数。这样在分析状态转移的时候,后续的最佳状态就已经知晓。
综上所述, dp[i][j] 表示:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数。
状态转移方程:
dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j];
如果当前位置的 dungeon[i][j] 是⼀个比较大的正数的话, dp[i][j] 的值可能变 成 0 或者负数。也就是最低点数会小于 1 ,那么骑士就会死亡。因此求出来的 dp[i] [j] 如果小于等于 0 的话,说明此时的最低初始值应该为 1 。
初始化和返回值:
在本题中,在 dp 表最后面添加一行,并且添加一列后,所有的值都先初始化为INT_MAX,然后让 dp[m][n - 1] = dp[m - 1][n] = 1 即可。
从右下往左上填值,最后返回 dp[0][0] 的值。
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m = dungeon.size(), n = dungeon[0].size();
// dp[i][j] 表示:从 [i, j] 位置出发,到达终点时所需的最低初始健康点数
vector<vector<int>> dp(m+1, vector<int>(n+1, INT_MAX)); // 多开一行一列
dp[m][n-1] = dp[m-1][n] = 1;
for(int i = m-1; i >= 0; --i)
{
for(int j = n-1; j >= 0; --j)
{
dp[i][j] = min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j];
if(dp[i][j] < 1)
dp[i][j] = 1;
}
}
return dp[0][0];
}
};
本篇完。
下一篇是有关哈希表的OJ,下下篇是简单多状态dp类型的动态规划。
标签:13,Offer,int,由易到难,路径,示例,vector,public,dp From: https://blog.csdn.net/GRrtx/article/details/136508974