一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
2 * 109
Related Topics
数学
动态规划
组合数学
首先可以确定机器人的位置,(0,0)。
然后我们知道机器人要去的位置是(m,n)
因为机器人只能往右走和下走,所以能确定机器人能走的步数为格子的纵轴长度-1加上横轴长度-1.
用递归模拟机器人的行驶路线。
会有如下几种情况
1)当机器人的的步数为0的时候
我们可以判断机器人的位置,
如果在目标位置那么我们可以确定我们找到了一种方法返回1,不在目标位置返回0.
2)当机器人来到纵轴的底部的时候这个时候只能往右走。
3)当机器人来到横轴的底部的时候这个时候只能往下走。
4)当机器人不在2),3)两种极端情况下,那么他既可以往右,也能往下走,递归这两种情况
因为递归会发生重复情况,所以添加一张缓存表缓存走过的位置。
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
return process(m - 1, n - 1, 0, 0, m + n - 2, dp);
}
public int process(int m, int n, int i, int j, int b, int[][] dp) {
int result;
if (dp[i][j] != 0) {
return dp[i][j];
}
if (b == 0) {
result = j == n && i == m ? 1 : 0;
} else if (i == m) {
result = process(m, n, i, j + 1, b - 1, dp);
} else if (j == n) {
result = process(m, n, i + 1, j, b - 1, dp);
} else {
result = process(m, n, i, j + 1, b - 1, dp) + process(m, n, i + 1, j, b - 1, dp);
}
dp[i][j] = result;
return result;
}
通过递归和dp表我们可以观察到机器人在最下方和最右方只能像一个方向移动,而不处于以上两种极端情况下。
机器人可以往下或者往左移动。
所以根据以上结论可以得出状态转移的公式为
dp[i][j] = dp[i + 1][j] + dp[i][j + 1]
当处于边界的情况下
对边界的值设置为1
public static int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for (int i = 0; i < n; i++) {
dp[m - 1][i] = 1;
}
for (int i = 0; i < m; i++) {
dp[i][n - 1] = 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];
}
动态规划的空间复杂度是n*n,而我们可以发现我们最后的有效值其实只需要最后的第一行的值
[6][3][1]
[3][2][1]
[1][1][1]
所以可以对空间进行优化,去掉二维数组中的列只保留单个一行的值
public int uniquePaths(int m, int n) {
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
dp[i] = 1;
}
for (int i = m - 2; i >= 0; i--) {
for (int j = n - 2; j >= 0; j--) {
dp[j] = dp[j] + dp[j + 1];
}
}
return dp[0];
}