一、题目描述
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
二、解题思路
解题思路一
机器人从(0,0)位置出发,到(m-1,n-1)是终点。
按照动规五部曲:
第一步:确定dp数组(dp table)以及下标的含义
dp[i][j]
表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。
第二步:确定递归公式
想要求dp[i][j]
,只能有两个⽅向来推导出来,即dp[i-1][j]
和 dp[i][j - 1]
。
此时在回顾⼀下 dp[i-1][j]
表示啥,是从(0, 0)的位置到(i - 1, j)有⼏条路径,dp[i][j - 1]同理。
那么很⾃然,dp[i][j] = dp[i-1][j] + dp[i][j-1]
,因为dp[i][j]
只有这两个⽅向过来。
第三步:dp数组初始化
如何初始化呢,⾸先dp[i][0]
⼀定都是1,因为从(0, 0)的位置到(i, 0)的路径只有⼀条,那么dp[0][j]
也同理。
所以初始化代码为:
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;
第四步:确定遍历顺序
这⾥要看⼀下递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
,dp[i][j]
都是从其上⽅和左⽅推导⽽来,那么从左到右⼀层⼀层遍历就可以了。
这样就可以保证推导dp[i][j]
的时候,dp[i-1][j]
和 dp[i][j-1]
⼀定是有数值的。
第五步:举例推导dp数组
如图所示:
代码演示一:
class Solution {
public int uniquePaths(int m, int n) {
//声明一个二维数组,用来存储路径数
int[][] dp = new int[m][n];
//数组初始化
for(int i=0; i<m; i++){
dp[i][0] = 1;
}
for(int j=0; j<n; j++){
dp[0][j] = 1;
}
for(int i=1; i<m; i++){
for(int j=1; j<n; j++){
//状态转移方程
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
时间复杂度:O(m*n)
空间复杂度:O(m*n)
解题思路二
看下面图,可以看出一共m,n的话无论怎么走,走到终点都需要m+n-2步:
在这m+n-2步中,一定有m-1步是要向下走的,n-1步向右走,不管什么时候走,怎么走,总的步数是不变的,所以这就变成了一个排列组合问题,转换成总共要走m+n-2步,往下走m-1步有多少中走法。公式如下:
排列组合计算公式:
本题公式为: (m+n-2)! / [(m-1)! * (n-1)!]
代码演示二:
class Solution {
public int uniquePaths(int m, int n) {
//总的结果数
int N = n + m - 2;
double res = 1;
for (int i = 1; i < m; i++){
//注意这里的计算分析
res = res * (N - (m - 1) + i) / i;
}
return (int) res;
}
}
时间复杂度:O(m)
空间复杂度:O(1)
这里分析一下为什么res = res * (N - (m - 1) + i) / i:
比如要计算:n!/(n-m)! ,那么可以把(n-m)!约掉【这里的m 、n与题无关】
相当于在n的阶乘中,后面的1 ~ n-m没了,只把n-(m+1)到n乘起来。
n! = n * (n-1) * (n-2) * ··· * (n-(m+1)) * (n-m) * (n-(m-1)) * ···* 3 * 2 * 1
然后后半段被约掉了: n * (n-1) * (n-2) * ··· * (n-(m+1)) *【 (n-m) * (n-(m-1)) * ···* 3 * 2 * 1】
只剩下: n * (n-1) * (n-2) * ··· * (n-(m+1))