路径问题
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.不同路径
题目链接:62. 不同路径
题目分析:
机器人每次只能向右或者向下走。问从起点到终点共有多少路径。
算法原理:
状态表示
根据经验 + 题目要求
经验都是以 i 位置为结尾,或者以 i 位置为起点,巴拉巴拉。但是这道题是二维的,所以 以[i,j]为结尾时,巴拉巴拉,题目要求问从起点到终点共有多少路径。所以
dp[i][j]表示,走到[i,j]位置的时候,一共有多少种方式。
状态转移方程
根据最近的一步,划分问题。
我们的问题是走到[i,j]的位置,那最近的一步就是如何走到[i,j]的位置,这道题只能向左向下走,因此走到[i,j]的位置分为两种情况。从[i-1,j]走到[i,j],从[i,j-1]走到[i,j]。然后就看这两种情况能不能用状态表示。
从[i-1,j]走到[i,j]仅需在走一步就到[i,j]了,所以到[i-1,j]有多少种方法就知道从上面到[i,j]有多少种方法。那到[i-1,j]有多少种方法呢。而dp[i][j]表示就是走到[i,j]位置的时候,一共有多少种方式。所以dp[i-1,j] 可以表示从上面到[i,j]有多少种方法。
同理从[i,j-1]走到[i,j]也仅需再走一步就到[i,j],dp[i,j-1] 表示从左边到[i,j]有多少种方法。
初始化
填表时不越界
根据状态转移方程填表我们发现填表需要用到上面的位置和左边的位置。但是填第一行和第一列就会发生越界的情况。
之前一维数组dp的时候说过有两种初始化的方式
- 填表前先把越界的地方初始化
- 一维数组前多开一个虚拟节点,繁琐的初始化就可以放在填表中进行,简单很多。
二维数组也可以这样做,直接多开一行一列! 然后填表是就不会越界了。
但是要注意两个问题
- 虚拟节点里面的值,要保证后面填表的结果是正确的
- 下标的映射
虚拟节点的值应该填多少,具体问题具体分析。
这道题从起点到起点应该是1,从起点向左这一行也都是1,向下这一列也是1,那虚拟节点值填多少可以保证是这样的结果呢,我们发现只要把dp[0][1]这个虚拟位置初始化为1,其他初始化为0就可以了。
建议初始化,一维数组dp多开一个虚拟节点初始化,二维数组dp多开一行一列初始化。
填表顺序
从上往下填写每一行,每一行从左向后
返回值
题目要返回的是mn的位置,因为我们刚好多开一行一列,所以直接把dp[m][n]返回即可。
class Solution { int dp[101][101]; public: int uniquePaths(int m, int n) { // 1.创建dp表 // 2.初始化 // 3.填表 // 4.返回值 dp[0][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]; } };
2.不同路径 II
题目链接:63. 不同路径 II
题目分析:
上面的题是在没有障碍的情况下求左上角到右下角有多少种路径。这道题加上了路径让求左上角到右下角有多少种路径。其实思路都是一样的。
算法原理:
1.状态表示
有上面基础,这道题我们直接就可以写出来
dp[i][j] 表示:到达 [i][j] 位置的时候,一共有多少种方法
2.状态转移方程
因为这道题有障碍物,所以我们多一步考虑障碍物的问题。因此考虑 dp[i][j] 本身就是障碍物,和不是障碍物的情况。如果本身就是障碍物从上从左到来不管什么情况,直接就是 0。如果不是障碍物,那就考虑从上从左来的情况,因为上道题已经说过了这种情况。所以直接 dp[i-1][j] + dp[i][j-1]。 可能你会想到为什么不考虑这两个位置是否为障碍的情况。注意如果它们为障碍就已经给过是0了,刚刚才说的。所有即使是都是障碍0+0也没关系。
3.初始化
dp表多开一行一列。
- 虚拟节点里面的值,要保证后面填表的结果是正确的
- 下标的映射
机器人刚开始位置一动不动就是1。所以可以选dp[0][1]或者 dp[1][0] 初始化为1 ,其余位置都为0,就能保证填表正确。
因为要从dp表反推到矩阵上,所以这道题尤其注意下标映射的关系。因为多少开一行一列下标整体往右下角移动一位,所以原来是(0,0) 变成了 (1,1),如果想从dp表找回之前矩阵的位置所以下标都要统一-1才行。i-1,j-1。
4.填表顺序
从上到下填写每一行,每一行从左往右
5.返回值
dp[m][n]
class Solution { public: int uniquePathsWithObstacles(vector<vector<int>>& nums) { // 1. int m = nums.size(), n = nums[0].size(); vector<vector<int>> dp(m+1, vector<int>(n+1)); dp[0][1] = 1; for(int i = 1; i <= m; ++i) for(int j = 1; j <= n; ++j) if(nums[i - 1][j - 1] == 1) dp[i][j] = 0; else dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; return dp[m][n]; } };
3.珠宝的最高价值
题目链接:LCR 166. 珠宝的最高价值
题目分析:
给一个二维数组,求从左上角到右上角拿到价值的最大值。
算法原理:
1.状态表示‘
以[i][j]为结尾,巴拉巴拉
dp[i][j]表示:到达 [i][j] 位置的时候,此时的最大值
2.状态转移方程
根据最近的一步,划分问题。
只能向右向下走,因此到达 [i][j] 位置要么从上面来要么从左边来。求达到 [i][j] 位置的最大值,应该先求出[i-1][j] 和 [i][j-1] 位置的价值最大值,然后再加上 [i][j]的价值。而dp[i][j]表示:到达 [i][j] 位置的时候,此时的最大值。所有最终状态转移方程式
3.初始化
保证填表时不越界。
如果是和原二维数组一样的大小,填第一行和第一列是肯定越界。要么填表之后把这些地方初始化。要么dp表多申请一行一列。
- 虚拟节点里面的值,要保证后面填表的结果是正确的
- 下标的映射
第一个要填的格子,肯定是自己本身价值,因此虚拟节点里面的值初始化都是0.
多开一行一列,从dp表往回推 i - 1, j -1。
4.填表顺序
从上到下填写每一行,每一行从左往右
5.返回值
dp[m][n]
class Solution { public: int jewelleryValue(vector<vector<int>>& frame) { // 1.创建dp表 // 2.初始化 // 3.填表 // 4.返回值 int m = frame.size(), n = frame[0].size(); vector<vector<int>> dp(m + 1, vector<int>(n + 1)); for(int i = 1; i <= m; ++i) for(int j = 1; j <= n; ++j) dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + frame[i - 1][j - 1]; return dp[m][n]; } };
4.下降路径最小和
题目链接:931. 下降路径最小和
题目分析:
给一个nxn矩阵,让返回从第一行下降到最后一行的最小和,如果当前位置是(row, col) 的下一个元素应当是 (row + 1, col - 1)、(row + 1, col) 或者 (row + 1, col + 1) 中选一个。
算法原理:
1.状态表示
经验 + 题目要求
dp[i][j] 表示:到达 [i][j] 位置时,最小的下降路径
2.状态转移方程
根据最近一步,划分问题
到达[i][j] 位置有三种情况,从[i-1][j-1],[i-1][j],[i-1][j+1]任意一个位置都可以到。但是我们要找的是到达 [i][j] 最小路径,那[i-1][j-1],[i-1][j],[i-1][j+1]一定要是最小。而dp[i][j] 表示:到达 [i][j] 位置时,最小的下降路径。然后在加上[i][j]本身的值。所以状态转移方程为
3.初始化
如果我们开辟和原数组一样大小的dp表,第一行和第一列和最后一列填表的时候都会越界。因此dp表可以多开一行多开两列。
- 虚拟节点里面的值,要保证后面填表的结果是正确的
- 下标的映射
虚拟节点的值应该放什么呢。先不考虑多加的行和列。没有这些的话,考虑这些填表越界的格子应该填多少。先看第一行,从第一行开始到达第一行当前位置最小下降路径和此时是没有选择的,只能是自己本身的值。正好代入状态转移方程是没问题的,因此第一行虚拟节点的值应该填0。再看第一列和最后一列,如果第一列和最后一轮还是0的话,可不可以?。,如果第一列和最后一列是0的话,我们要的是三者中的最小值,在填表的时候就影响到上面经过计算得到的正确结果,因此第一行和第一列不能是0,我们可以给一个+∞。
因为dp表多加一行两列整是往右下移动一位的。填表的时候要从dp表找到原始矩阵的值,必须i-1,j-1。
4.填表顺序
从上到下填写每一行,每一行从左往右
5.返回值
找到最后一行的最小值
class Solution { public: int minFallingPathSum(vector<vector<int>>& matrix) { // 1.创建dp表 // 2.初始化 // 3.填表 // 4.返回值 int n = matrix.size(); vector<vector<int>> dp(n + 1, vector<int>(n + 2, INT_MAX)); // 初始化第一行 for(int j = 0; j < n + 2; ++j) dp[0][j] = 0; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) dp[i][j] = min(min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i - 1][j + 1]) + matrix[i - 1][j - 1]; // 返回值 int ret = INT_MAX; for(int j = 1; j <= n; ++j) ret = min(ret, dp[n][j]); return ret; } };