给定一个二维数组,每格为0/1值,1代表无法通过。求从左上到右下的不同路径数。只能往右/下走。
Input: [ [0,0,0], [0,1,0], [0,0,0] ] Output: 2 Explanation: There is one obstacle in the middle of the 3x3 grid above. There are two ways to reach the bottom-right corner: 1. Right -> Right -> Down -> Down 2. Down -> Down -> Right -> Right
该题较62题多了个障碍的概念。方法相似,只要将障碍处的dp设为0即可(带障碍说明其后续的路径必然不会经过这里),依然可知dp式子:dp [i] [j] = dp [i-1] [j] + dp [i] [j-1],但是需要考虑几种情况:
情况(1),在第一行或者第一列中出现了障碍。此时易知,第一行自障碍开始到后面,路径数都为0(因为此路不通,见(1)的第一行)。第一列同理。
情况(2)(3)则是起点/终点本身无法通过,结果为0。
基于上面的情况,两步走:
(1)初始值的设定:第一行与第一列,若不为障碍,则设为1,若为障碍,则设为0
先将整个路径数矩阵dp置为0,然后对第一行/列进行初始化。若不为障碍,则设为1,若为障碍,则设为0,且跳出循环,这样后面的几个dp值即为初始值0。
vector<vector<int>> dp (m, vector<int>(n, 0)); // 构造二维数组存储每一处的dp值
for(int i=0; i<m; i++) { // 为第一列初始化
if(obstacleGrid[i][0] == 1) { // 若当前格值为1,则dp值为0,并且之后的值为初始值0
// dp[i][0] = 0; // 初始值即为0,此行可注释掉
break; // 跳出后,此后的元素则为初始值0
}
dp[i][0] = 1;
}
for(int i=0; i<n; i++) { // 第一行,同第一列
if(obstacleGrid[0][i] == 1) {
// dp[0][i] = 0;
break;
}
dp[0][i] = 1;
}
(2)dp公式:dp [i] [j] = dp [i-1] [j] + dp [i] [j-1],进行求解。
for(int i=1; i<m; i++) {
for(int j=1; j<n; j++) {
if(obstacleGrid[i][j] == 1) { // 若当前格值为1,则dp值为0
dp[i][j] = 0;
continue;
}
dp[i][j] = dp[i-1][j] + dp[i][j-1]; // 每一处与左侧即上侧相关。
}
}
(3)返回最后一个即为到达右下角的路径数。
总代码:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
if(obstacleGrid.size() == 0)
return 0;
int m = obstacleGrid.size(); // 行数
int n = obstacleGrid[0].size(); // 列数
if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) // 起点/终点为1,无法通过,则路径数为0
return 0;
vector<vector<int>> dp (m, vector<int>(n, 0)); // 构造二维数组存储每一处的dp值
for(int i=0; i<m; i++) { // 为第一列初始化
if(obstacleGrid[i][0] == 1) { // 若当前格值为1,则dp值为0,并且之后的值为初始值0
// dp[i][0] = 0; // 初始值即为0,此行可注释掉
break;
}
dp[i][0] = 1;
}
for(int i=0; i<n; i++) { // 第一行,同第一列
if(obstacleGrid[0][i] == 1) {
// dp[0][i] = 0;
break;
}
dp[0][i] = 1;
}
for(int i=1; i<m; i++) {
for(int j=1; j<n; j++) {
if(obstacleGrid[i][j] == 1) { // 若当前格值为1,则dp值为0
dp[i][j] = 0;
continue;
}
dp[i][j] = dp[i-1][j] + dp[i][j-1]; // 每一处与左侧即上侧相关。
}
}
return dp[m-1][n-1];
}
优化内存使用空间,使用一维数组即可。
(1)设置初始值:先为第一行进行初始化,若为障碍,则dp值为0,且后续都为0。
(2)DP公式:dp[j] = dp[j-1] + dp[j]。行从第一行开始,列元素从第0个开始。所以要考虑第一列上,若上一个元素为0时的情况。
总代码:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
if(obstacleGrid.size() == 0)
return 0;
int m = obstacleGrid.size(); // 行数
int n = obstacleGrid[0].size(); // 列数
if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) // 起点/终点为1,无法通过,则路径数为0
return 0;
vector<int> dp (n, 0); // 构造一维数组存储dp值
for(int i=0; i<n; i++) { // 为第一行初始化
if(obstacleGrid[0][i] == 1) { // 若当前格值为1,则dp值为0,并且之后的值为初始值0
// dp[i][0] = 0; // 初始值即为0,此行可注释掉
break;
}
dp[i] = 1;
}
for(int i=1; i<m; i++) {
for(int j=0; j<n; j++) {
if(obstacleGrid[i][j] == 1) { // 若当前格值为1,则dp值为0
dp[j] = 0;
continue;
}
if(j == 0) { // 对于第一列的元素
if(dp[j] == 0) // 若上侧为0,则当前也为0
continue;
dp[j] = 1;
}
dp[j] = dp[j-1] + dp[j]; // 每一处与左侧即上侧相关。
}
}
return dp[n-1];
}