LeetCode[62] 不同路径(DP)
本题目算是面试比较常见的了,主要考察对算法的理解,最优的解法是动态规划,本篇写一下本题的动态规划思路和解法。
题目描述
一个机器人位于一个 m x n 网格的左上角(起始点为图中的 “Start”),机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(图中标记为 “Finish”)。问总共有多少条不同的路径?
示例一:
输入:m = 3, n = 2
输出:3
解释:从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例二:
输入:m = 7, n = 3
输出:28
提示:
- 1 <= m, n <= 100
- 题目数据保证答案小于等于 2 * 10^9 (int类型)
题解思路
- 暴力法:使用 DFS 遍历,从 (1,1)-> (m, n),当到达右下角时,计数加一,直到遍历结束。只能解决一部分用例,会执行超时,时间复杂度 O(N^2)
- 动态规划:开辟新的状态空间,保存每个点的状态值,该状态值表示到达该点所能走的路数。最终到达右下角时,该点所记录的路数就是总共的不同路径数。因为只需要遍历一次二维数组的状态值,因此时间复杂度为 O(N)
- DFS & DP:看到一个有意思的题解,对 dfs 做了优化,使用状态转移方程去除重复计算,因此时间复杂度同 dp
- DP最优解法:优化存储空间,使用一维数组存储状态。空间复杂度从 O(m x n)降到 O(n)
如何求每个点的状态值呢?这个问题就用到了数学归纳法了,观察可得:
当 m = 1, n = 1 时,起点不用向右和向下就已到达,路数为 1;
当 m = 1, n = 2 时,起点只能向下一步到达目标点,路数为 1;
当 m = 2, n = 1 时,起点只能向右一步到达目标点,路数为 1;
当 m = 2, n = 2 时,起点可以往右一步再往下一步,或者先往下再往右到达目标点,路数为 2;
当 m = 2, n = 3 时,起点可以往下两步再往右一步,或者到达点(2,2)再往下一步,
而到达点(2,2)的路数为 2,因此到达目标点的路数总共 为 3;
以此类推,可以发现规律,到达点(m, n)的路数是(m - 1, n)的路数加(m, n - 1)的路数和,
即:status[m][n] = status[m - 1][n] + status[m][n-1](状态转移方程)
代码实现
递归代码
int status[101][101] = {0};
int uniquePaths(int m, int n)
{
if(m <= 0 || n <= 0) {
return 0;
}
if(m == 1 || n == 1) {
return 1;
}
// 当该点状态值 > 0 时,说明已经计算过路径数,无需重复计算
if(status[m][n] > 0) {
return status[m][n];
}
status[m - 1][n] = uniquePaths(m - 1, n);
status[m][n - 1] = uniquePaths(m, n - 1);
status[m][n] = status[m - 1][n] + status[m][n - 1];
return status[m][n];
}
迭代代码
int uniquePaths(int m, int n)
{
int status[101][101] = {0};
int i, j;
for(i = 1; i <= m; i++) {
for(j = 1; j <= n; j++) {
if(i == 1 || j == 1) {
status[i][j] = 1;
}
else {
status[i][j] = status[i - 1][j] + status[i][j -1];
}
}
}
return status[m][n];
}
DFS优化解法
int status[101][101];
int dfs(int x, int y) {
if(x < 1 || y < 1) {
return 0;
}
if(x == 1 || y == 1) {
return 1;
}
if(status[x][y] > 0) {
return status[x][y];
}
status[x][y] = dfs(x - 1, y) + dfs(x, y - 1);
return status[x][y];
}
int uniquePaths(int m, int n) {
return dfs(m, n);
}
DP最优解法
int uniquePaths(int m, int n)
{
int *status = (int *)malloc(sizeof(int) * n);
int i, j;
for(i = 0; i < n; i++) {
status[i] = 1;
}
for(i = 1; i < m; i++) {
for(j = 1; j < n; j++) {
status[j] += status[j - 1];
}
}
return status[n - 1];
}
所有博客均在CSDN首发 Caso_卡索 https://blog.csdn.net/xiaoma_2018