1. 问题描述:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 10 ^ 9
2. 思路分析:
① 首先对于这种尝试有多少条路径的经典问题我们可以使用递归来进行求解(递归的特点就是适这些路径的探索性的问题),因为在递归的时候可以尝试所有的路径,因为每一次都是存在两种可能的走法,一种是向右走,一种是向下走,对应两种平行状态,首先可以考虑没有返回值的递归求解,这样可以使用一个全局变量来记录路径的数目,当走到终点的时候对路径数目加1即可,但是在递归的时候存在着子问题的重复求解,因为在递归返回到上一层的时候尝试其他位置往下递归的过程中会递归到之前已经求解过的位置而当到达之前已经求解过的位置的是那么又会像之前递归递归求解当前的位置所以存在着子问题的重复求解。
② 对于①中子问题的重复求解我们可以其实可以使用记忆型的递归进行求解(记忆型递归一般使用数据结构来存储状态,当我们递归的时候发现当前的状态之前已经求解过的时候那么就可以直接返回之前记录的值),递归的方法存在两个动态变化的参数所以我们可以使用二维的数组或者列表来记录位置(x, y)往下递归的路径数目。当我们发现(x, y)位置已经求解过了那么就可以直接返回这个位置记录的值即可。对于当前的位置存在两种走法,所以将这两种走法加起来的数目就是其余位置到达当前位置的路径数目,这样一直返回到第一个位置那么就可以求解出从(1,1)到终点的路径数目。
③ 除了使用递归的思路解决之外,我们还可以使用动态规划来求解,当前位置路径的数目等于左边的格子与右边格子的路径数目的总和,当循环结束之后返回最后一个格子的数目即可,这个还是比较好理解的。
3. 代码如下:
记忆型的递归:
import java.util.Arrays;
public class Solution {
static int count = 0;
/*使用数组来记录中间结果*/
static int rec[][];
public int uniquePaths(int m, int n) {
rec = new int[m][n];
for (int i = 0; i < m; ++i){
/*将记录数组填充为-1*/
Arrays.fill(rec[i], -1);
}
return dfs(0, 0, m, n);
}
public static int dfs(int row, int col, int m, int n) {
if (row == m - 1 && col == n - 1) return 1;
if (row >= m || col >= n) return 0;
/*注意判断之前是否已经求解过值的代码那么需要放在判断是否越界的代码后面因为上面避免了数组越界的问题*/
if (rec[row][col] != -1) return rec[row][col];
/*使用一个变量来记录路径的数目*/
int count = 0;
/*两个平行状态一个是向右边走一个是向下边走*/
count += dfs(row, col + 1, m, n);
count += dfs(row + 1, col, m, n);
/*将记录数组中的值返回*/
rec[row][col] = count;
return count;
}
}
2021/6/17更新:
from typing import List
class Solution:
def dfs(self, x: int, y: int, m: int, n: int, rec: List[List[int]]):
if x >= m or y >= n: return 0
if rec[x][y] != -1: return rec[x][y]
if x == m - 1 and y == n - 1: return 1
r = self.dfs(x, y + 1, m, n, rec)
d = self.dfs(x + 1, y, m, n, rec)
# 当前位置往下递归的路径数目为r + d并且将其记录到rec列表中
rec[x][y] = r + d
return rec[x][y]
def uniquePaths(self, m: int, n: int) -> int:
# rec是记录列表
rec = [[-1] * n for i in range(m)]
return self.dfs(0, 0, m, n, rec)
dfs代码如下:
public class Solution {
int count = 0;
public int uniquePaths(int m, int n) {
dfs(0, 0, m, n);
return count;
}
/*对于这种求解多少条路径的可以使用dfs来解决
* 因为向右边走与向下边走不存在重复的路径也就是说每一条路径只会被走一次
* */
public void dfs(int row, int col, int m, int n) {
/*可以使用两个变量的增减来模拟数组中移动位置的过程*/
if (row == m - 1 && col == n - 1){
count++;
return;
}
if (row >= m || col >= n) return;
/*两个平行状态一个是向右边走一个是向下边走*/
dfs(row, col + 1, m, n);
dfs(row + 1, col, m, n);
}
}
动态规划代码:
import java.util.Arrays;
public class Solution {
public int uniquePaths(int m, int n) {
if(m==0||n==0)
return 0;
int dp[m][n];
int i=0,j=0;
dp[0][0]=1; //dp[i][j]数组表示到达(i,j)位置总共有多少种路径
/**边界赋值**/
for(i=1;i<n;i++){
dp[0][i]=1;
}
for(j=1;j<m;j++){
dp[j][0]=1;
}
for(i=1;i<m;i++){
for(j=1;j<n;j++) {
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}