今天我们来初步入门一下动态规划,来讲一下力扣非常经典的两个题,也是动态规划的入门题,不同路径1和2。
关于动态规划五部曲
- 确定dp数组含义
- 初始化
- 递推公式
- 遍历顺序
- 打印答案
相信大家已经并不陌生,我们来先看一下比较简单的不同路径1
先看题目
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
看到题目有些同学就想到直接dfs,显然dfs肯定是可以的,但提交就会超时,所以在此我们不做过多讲解,今天我们做这个题的思路是动态规划,既然是动态规划,我们就要按照流程走。首先,我们确定dp数组含义,题目显而易见要用一个二维的dp数组来存储每一个格子的状态,含义就是走到这个点一共有多少种方法,接下来看一下递推公式,由于每次只能是向下或者向右移动,所以可以确定我们的递推公式
dp[i][j]=dp[i-1][j]+dp[i][j-1]
看到这时候肯定有同学对此有些疑惑,为什么不是dp[i-1][j]+1呢?注意了我们要求的是有多少种不同的方式,而不是一共要走多少步。
接下来就是初始化,这也是很多同学在这个题犯错的一点。可以注意到我们要推到dp[i][j]的状态必须知道其上方和右方的状态,所以第一行和第一列必须全部初始化,初始化为多少呢?很多同学就有下面的想法
for(int i=0;i<n;i++){
dp[0][i]=i;
}
for(int j=0;j<m;j++){
dp[j][0]=j;
}
这显然是错误的,而错误的点也很显而易见,就是忘记了我们要求的是路径而不是步数,所以正确的初始化应该全部初始化为1
for(int i=0;i<n;i++){
dp[0][i]=1;
}
for(int j=0;j<m;j++){
dp[j][0]=1;
}
就此我们动态规划五部曲已经完成了,下面放出ac代码
class Solution {
public:
int uniquePaths(int m, int n) {
int dp[m][n];
for(int i=0;i<n;i++){
dp[0][i]=1;
}
for(int j=0;j<m;j++){
dp[j][0]=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-1][n-1];
}
};
而不同路径2的基本思路和1没什么不同,就是加了障碍,也就是加了几个判断条件,我们先来看题目
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
首先看到障碍物我们应该想到,是不是障碍物在某个地方会导致直接走不通?答案就是在起点和终点如果有障碍物,我们直接返回值0。
dp数组的含义不做过多说明,对于递推公式,大体和第一题是差不多的,但是注意如果有障碍物,我们就走不通了,也就是说,如果这个点有障碍物,我们就直接视为0,这样的话假如他的右面dp[i+1][j]就是0+dp[i+1][j-1]。
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if (obstacleGrid[i][j] == 1) continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
初始化我们依旧是仅需要初始化第一列和第一行,但是要注意,在第一列和第一行,一旦遇到了障碍物,后面的就直接全部视为0,相信这个是比较好理解的,因为只要前面有障碍物,就没有路径能到达,来看一下初始化代码
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
遍历顺序和打印我们就不多说了,和上面的一个样。来看一下AC代码
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1)
return 0;
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if (obstacleGrid[i][j] == 1) continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m-1][n-1];
}
};
上面两题还是比较重要的,是动态规划的入门题,今年也有几个公司面试的时候出手撕了,所以大家一定要会做。
今后我会继续更新力扣(主要针对于面试),蓝桥杯,c++高性能服务器,unity,计算机网络和数据结构的知识。