62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
思路:动态规划
自底向上的算法,当到达Finnish之前,总共有两格可以一步到达Finnish,所以只需要将到达这两格的路径数加起来就可以了,纵观全图,每个所到达格子的路径数就是与他相邻的左边的格子的路径数加上到达上边的格子的路径数;
所以将整个网格看做一个二维数组,每个位置存储的既是到达此位置的路径数;
因为最上边的格子和最下边的格子无论如何都只有一条路线,所以先初始化第一行和第一列为1;
由此,DP方程为:
dp[i,j] = dp[i-1,j] + dp[i,j-1];
代码:时间复杂度o(mn) 空间复杂度o(mn)
public int uniquePaths(int m, int n) {
int[][] result = new int[m][n];
for (int i = 0; i < n; i++) {
result[0][i] = 1;
}
for (int i = 0; i < m; i++) {
result[i][0] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
result[i][j] = result[i - 1][j] + result[i][j - 1];
}
}
return result[m - 1][n - 1];
}
优化,用一个一维数组存储每行的当前数组
时间复杂度为o(m*n)
空间复杂度为o(n)
public int uniquePaths2(int m, int n) {
int[] cur = new int[n];
Arrays.fill(cur, 1);//将数组每个元素设置为1
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
cur[j] += cur[j - 1];
}
}
return cur[n - 1];
}
63. 不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
思路:与上题思路一致,只不过初始化第一行和第一列时有改变:
当第一行(或第一列)任意格子一旦有障碍物时,其右面(下面)的格子均视为有障碍物,因为无论如何都是到达不了的。当然输入的时候1代表障碍物,初始化之后0代表到达不了(因为初始化之后数值代表的是到达所在格子的路径和)
DP方程不变,只是当遇到障碍物时,直接置0
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int R = obstacleGrid.length;
int C = obstacleGrid[0].length;
if (obstacleGrid[0][0] == 1) {
return 0;
}
obstacleGrid[0][0] = 1;
for (int i = 1; i < R; i++) {
obstacleGrid[i][0] = (obstacleGrid[i][0] == 0 && obstacleGrid[i - 1][0] == 1) ? 1 : 0;
}
for (int i = 1; i < C; i++) {
obstacleGrid[0][i] = (obstacleGrid[0][i] == 0 && obstacleGrid[0][i - 1] == 1) ? 1 : 0;
}
for (int i = 1; i < R; i++) {
for (int j = 1; j < C; j++) {
if (obstacleGrid[i][j] == 0) {
obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1];
} else {
obstacleGrid[i][j] = 0;
}
}
}
return obstacleGrid[R - 1][C - 1];
}
64. 最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
思路:
自底向下,新建一个和原数组一样大小的二维数组,每个元素代表到Finnish的最小路径权值,Finnish位置初始为原数组元素大小,每个元素值等于当前所在格子的权值加上其右边和下边中较小者;
最后一行与最后一列的初始化为,权值加上上一个格子的值(累加)
DP方程:
dp[i,j] = grid[i,j] + Math.min(dp[i+1,j] , dp[i,j+1]);
代码:
public int minPathSum1(int[][] grid) {
int[][] dp = new int[grid.length][grid[0].length];
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if (i == grid.length - 1 && j != grid[0].length - 1) {
dp[i][j] = grid[i][j] + dp[i][j + 1];
}else if (j == grid[0].length - 1 && i != grid.length - 1) {
dp[i][j] = grid[i][j] + dp[i + 1][j];
}else if (j != grid[0].length - 1 && i != grid.length - 1) {
dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
}else {
dp[i][j] = grid[i][j];
}
}
}
return dp[0][0];
}
一维数组优化,从状态转移方程中可得,只需记录与之相关的右边的和下边的节点即可,数组从最后一行开始记录每行的值;
DP方程为:
dp[j] = grid[i,j] + Math.min(dp[j],dp[j+1])
代码:
public int minPathSum2(int[][] grid) {
int[] dp = new int[grid[0].length];
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if (i == grid.length - 1 && j != grid[0].length - 1) {
dp[j] = grid[i][j] + dp[j + 1];
}else if (j == grid[0].length - 1 && i != grid.length - 1) {
dp[j] = grid[i][j] + dp[j];
}else if (j != grid[0].length - 1 && i != grid.length - 1) {
dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]);
}else {
dp[j] = grid[i][j];
}
}
}
return dp[0];
}
不需要额外的空间优化
直接在原有的二维数组上改
代码:
public int minPathSum3(int[][] grid) {
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if (i == grid.length - 1 && j != grid[0].length - 1) {
grid[i][j] = grid[i][j] + grid[i][j + 1];
}else if (j == grid[0].length - 1 && i != grid.length - 1) {
grid[i][j] = grid[i][j] + grid[i + 1][j];
}else if (j != grid[0].length - 1 && i != grid.length - 1) {
grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j], grid[i][j + 1]);
}
}
}
return grid[0][0];
}