一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
答案:
1public int uniquePaths(int m, int n) {
2 return uniquePaths(1, 1, m, n);
3}
4
5public int uniquePaths(int i, int j, int m, int n) {
6 if (i > m || j > n) return 0;
7 if ((i == m && j == n)) return 1;
8 int right = uniquePaths(i + 1, j, m, n);
9 int down = uniquePaths(i, j + 1, m, n);
10 return right + down;
11}
解析:
每次走一步,并且只能向右或向下,这种递归的解法是最简单也是最容易理解的,但却是最耗时的,效率很差,我们再来看一种解法
1public int uniquePaths(int m, int n) {
2 int[][] path = new int[m][n];
3 for (int i = 0; i < path.length; i++) {
4 Arrays.fill(path[i], 1);
5 }
6 for (int i = 1; i < m; i++)
7 for (int j = 1; j < n; j++)
8 path[i][j] = path[i - 1][j] + path[i][j - 1];
9 return path[m - 1][n - 1];
10}
11
这种解法很好理解,因为走到当前位置要么从上面一步下来,要么从左边走一步过来,他俩相加就是走到当前位置的路径和。其实这种效率也不是很高,因为我们要计算当前位置的路径和,我们只需要知道他上面的一步和左边一步的路径数量即可,没必要记录所有的,我们还可以优化一下,看下代码。
1public int uniquePaths(int m, int n) {
2 if (m > n)
3 return uniquePaths(n, m);
4 int[] cur = new int[m];
5 Arrays.fill(cur, 1);
6 for (int j = 1; j < n; j++)
7 for (int i = 1; i < m; i++)
8 cur[i] += cur[i - 1];
9 return cur[m - 1];
10}
这个理解起来稍微有点费劲,我们来慢慢分析,我们看下cur[i] += cur[i - 1];语句在两个for循环下面,这个语句中确没有j变量,我们可以这样理解,他是一行一行的遍历,语句我们可以改为cur[i] = cur[i]+cur[i - 1];第二个cur[i]相当于上一行的同一列记录的路径数,cur[i - 1]相当于这一行的左边那个点的路径和,加起来正好是当前路径和,而且这种写法也比较节省内存。接着往下看
1public int uniquePaths(int m, int n) {
2 int N = n + m - 2;
3 int k = m - 1;
4 double res = 1;
5 for (int i = 1; i <= k; i++)
6 res = res * (N - k + i) / i;
7 return (int) res;
8}
我们要想到达终点,往下走n-1步,往右走m-1步,总共需要走n+m-2步。这就相当于一个排列组合,(m+n-2)! / [(m-1)! * (n-1)!],所以结果为C = ( (n - k + 1) * (n - k + 2) * ... * n ) / (m-1)!