这是一道典型的动态规划问题,状态转移方程也比较直接,以下列出三种优化方式,优化空间复杂度
1.状态转移方程
令dp[i][j] 是到达 i, j 最多路径
这道题我们从终点[m-1][n-1]这个target倒推,动态方程:dp[i][j] = dp[i+1][j] + dp[i][j+1]
对于最后一行 dp[m-1][j],和最右侧一列 dp[i][n-1],由于都是在边界,所以只能为 1
时间复杂度:O(m*n)
空间复杂度:O(m * n)
优化:因为我们每次只需要 dp[i+1][j],dp[i][j+1]就好
class Solution {
//方法一:空间复杂度 O(m*n)
public int uniquePaths(int m, int n) {
if(m<=1 || n<=1) return 1; //首先,如果是只有一行或列,那肯定是1
int[][] dp = new int[m][n];
for(int i=0;i<m;i++) dp[i][n-1] = 1;
for(int j=0;j<n;j++) dp[m-1][j] = 1;
for(int i=m-2;i>=0;i--){ //终点倒退
for(int j=n-2;j>=0;j--){
dp[i][j] = dp[i+1][j]+dp[i][j+1];
}
}
return dp[0][0];
}
// //方法二:空间复杂度(2n)
public int uniquePaths(int m, int n) {
if(m<=1 || n<=1) return 1;
int[] pre = new int[n];
int[] cur = new int[n];
Arrays.fill(pre,1);
Arrays.fill(cur,1);
for(int i=m-2;i>=0;i--){
for(int j=n-2;j>=0;j--){
cur[j] = cur[j+1]+pre[j];
}
pre = cur.clone();
}
return cur[0];
}
//方法三:空间复杂度(n)
public int uniquePaths(int m, int n){
if(n<=1 || m<=1) return 1;
int[] cur = new int[n];
Arrays.fill(cur,1);
for(int i=m-2;i>=0;i--){
for(int j=n-2;j>=0;j--){
cur[j]+=cur[j+1];
}
}
return cur[0];
}
}
另一道leetcode63也是类似,只不过加了个障碍物,由于反向推到需要先判断是否是单列/行,这在障碍物变题中会比较麻烦,这次我们用正向推导状态转移方程,思路几乎一模一样。
1、状态定义: dp[i][j] 表示走到格子 (i, j) 的方法数。
2、状态转移: 如果网格 (i, j) 上有障碍物,则 dp[i][j]值为 0,表示走到该格子的方法数为0; 否则网格 (i, j) 可以从网格 (i - 1, j)或者 网格 (i, j - 1) 走过来,因此走到该格子的方法数为这两种方法数之和,即 dp[i, j] = dp[i - 1, j] + dp[i, j - 1]
状态转移方程如下:
3、初始条件
第 1 列的格子只有从其上边格子走过去这一种走法,因此初始化 dp[i][0] 值为 1,存在障碍物时为 0;
第 1 行的格子只有从其左边格子走过去这一种走法,因此初始化 dp[0][j] 值为 1,存在障碍物时为 0。
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length, n=obstacleGrid[0].length;
if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1) return 0;
int[][] dp = new int[m][n];
for(int i=0;i<m;i++){
if(obstacleGrid[i][0]==1) break;
dp[i][0] = 1;
}
for(int j=0;j<n;j++){
if(obstacleGrid[0][j]==1) break;
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];
}
}