最后更新
四刷?
依然是尝试用一维DP。
每次更新DP的时候相当于直接添加了2个方向的贡献值。
开始的dp[1] = 1不好理解,其实是代表每行第一个格子dp[j]来自左边的贡献值,来自上面的贡献值是他本身,我们在上一行算过,所以是dp[j] + 1。
下个格子dp[j+1]就变成了来自左边的贡献值 —— dp[j](刚刚算的) + 来自上面的贡献值 dp[j+1], 所以是dp[j+1] + dp[j]。。
如果是障碍物直接是0,没啥可说的。
Time: O(mn)
Space: O(min(m,n))
public class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
if (obstacleGrid.length == 0) return 0;
int row = obstacleGrid.length;
int col = obstacleGrid[0].length;
int[] dp = new int[col + 1];
dp[1] = 1;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (obstacleGrid[i][j] == 1) {
dp[j+1] = 0;
} else {
dp[j+1] += dp[j];
}
}
}
return dp[col];
}
}
一刷
小机器人走路的变体,典型DP。
现在加了个条件,有障碍,那无非是有障碍的那格是0.
public class Solution
{
public int uniquePathsWithObstacles(int[][] grid)
{
if(grid.length == 0) return 0;
int[][] dp = new int[grid.length][grid[0].length];
dp[0][0] = grid[0][0] == 1? 0:1;
if(dp[0][0] == 0)
return 0;
for(int i = 1; i < dp[0].length;i++)
{
if(grid[0][i] == 1) dp[0][i] = 0;
else dp[0][i] = dp[0][i-1];
}
for(int i = 1; i < dp.length;i++)
{
dp[i][0] = grid[i][0] == 1? 0:dp[i-1][0];
}
for(int i = 1; i < dp.length;i++)
for(int j = 1; j < dp[0].length;j++)
{
dp[i][j] = grid[i][j] == 1? 0:(dp[i-1][j] + dp[i][j-1]);
}
return dp[grid.length-1][grid[0].length-1];
}
}
Extra Space是n²,实际上不用,只需要2列,或者2行就行了。
dp[i][j]是从当前列+前一列 得出来的,所以只记录这俩就够了。
但是具体怎么实现没写代码,三刷再说吧。。
三刷。
二维DP记录就不说了。说说如何把m * n空间变成 M..
到dp[m][n]的解来自于dp[m-1][n]和dp[m][n-1]
遍历的时候是一行一行(一列一列)遍历的。
dp[1][1]来自于dp[1][0]
dp[2][1]来自于dp[2][0]
dp[3][1]来自于dp[3][0]
..这是每个点更新时来自于横向或者纵向其中一个方向的贡献,然后还有另一个方向。
问题是终归他们是相加的,不如先横向都加了,再总向把所有的和加起来。
另一个地方是初始化,如果是0的话,需要判断一下最外层;这里手动添加了半圈,结果不影响。
Time: O(nm)
Space: O(n)
public class Solution {
public int uniquePathsWithObstacles(int[][] grid) {
if (grid.length == 0) return 0;
int[] dp = new int[grid[0].length + 1];
dp[1] = 1;
for (int i = 1; i <= grid.length; i++) {
for (int j = 1; j <= grid[0].length; j++) {
if (grid[i-1][j-1] == 0) {
dp[j] += dp[j - 1];
} else {
dp[j] = 0;
}
}
}
return dp[grid[0].length];
}
}