问题:
A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?
Note: m and n will be at most 100.
代码示例:
1、分治
Robot每次都可以选择向下走一步,或者向右走一步。
因此每个位置的结果,都可以表示为:
从当前位置向下走一步的结果,与从当前位置向右走一步的结果之和。
这其实就是分治的思想,代码如下:
public class Solution {
public int uniquePaths(int m, int n) {
//m或n <= 0 时,结果为0
if (m <= 0 || n <= 0) {
return 0;
}
//m或n == 1时,只能一直向右,或者一直向下
//于是只有1种走法
if (m == 1 || n == 1) {
return 1;
}
//向右走一步的结果,加上向下走一步的结果
return uniquePaths(m, n-1) + uniquePaths(m-1, n);
}
}
这种方法是可行的,但递归相当耗时,
因为其中会进行大量的重复计算。
我们以m、n都为3举例,递归过程如下图所示:
图中红色部分即为重复计算的地方。
显然m、n越大,重复计算的部分越大,越耗时。
在自己的电脑上试了一下,当m、n均为100时,耗时40min还没有搞定 。
2、动态划归
既然使用分治这种正面拆解的方法,不能有效解决问题,
那么我们可以逆向思考,用小结果拼凑出大结果,即动态划归的思想。
思路如下图所示:
从图中可以看出(看颜色标记的区域),每个位置都等于:
同一行前一列的结果,与同一列前一行结果的和。
这其实就是分治的逆过程。
不过我们可以记录每个位置的值,直接计算求得结果,省去重复计算的麻烦。
代码如下:
public class Solution {
public int uniquePaths(int m, int n) {
//(m+1)行,(n+1)列
//第0行,第0列均置为0
int[][] rst = new int[m+1][n+1];
for (int j = 0; j < n+1; j++) {
rst[0][j] = 0;
}
for (int i = 0; i < m+1; ++i) {
rst[i][0] = 0;
}
//m、n均为1时,结果为1
rst[1][1] = 1;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <=n; ++j) {
//rst[i][j]初始为0
//为了兼容i=1,j=1的场景,写成下列公式
rst[i][j] = rst[i][j-1] + rst[i-1][j] + rst[i][j];
}
}
//返回结果
return rst[m][n];
}
}
m、n均为100时(结果已经越界,需要将int替换为long),耗时1ms都不到。