leetcode 62. 不同路径-动态规划及优化,双100%
62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
提示:
- 1 <= m, n <= 100
- 题目数据保证答案小于等于 2 * 10 ^ 9
解题思路
由于我们的目的是从左上角到右下角一共有多少种路径,那我们就定义 dp[i][j]
的含义为:当机器人从左上角走到(i, j) 这个位置时,一共有 dp[i][j]
种路径。 那么,dp[m-1][n-1]
就是我们要的答案了。
步骤
找出关系数组元素间的关系式想象以下,机器人要怎么样才能到达 (i, j)
这个位置?由于机器人可以向下走或者向右走,所以有两种方式到达一种是从 (i-1, j)
这个位置走一步到达一种是从(i, j - 1)
这个位置走一步到达因为是计算所有可能的步骤,所以是把所有可能走的路径都加起来,所以关系式是 dp[i][j] = dp[i-1][j] + dp[i][j-1]
。
找出初始值显然,当 dp[i][j]
中,如果 i 或者 j 有一个为 0,那么还能使用关系式吗?答是不能的,因为这个时候把 i - 1 或者 j - 1,就变成负数了,数组就会出问题了,所以我们的初始值是计算出所有的 dp[0][0….n-1]
和所有的 dp[0….m-1][0]
。
这个还是非常容易计算的,相当于计算机图中的最上面一行和左边一列。因此初始值如下:
dp[0][0…n-1] = 1;
// 相当于最上面一行,机器人只能一直往左走
dp[0…m-1][0] = 1;
// 相当于最左面一列,机器人只能一直往下走
class Solution
{
public:
int uniquePaths(int m, int n)
{
if (m <= 0 || n <= 0)
{
return 0;
}
if (m == 1 || n == 1)
{
return 1;
}
vector<vector<int>> dp(m, vector<int>(n)); //创建大小为m*n的数组
// 初始化
for (int i = 0; i < m; i++)
{
dp[i][0] = 1;
}
for (int i = 0; i < n; i++)
{
dp[0][i] = 1;
}
// 推导出 dp[m-1][n-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];
}
};
优化
根据公式 dp[i][j] = dp[i-1][j] + dp[i][j-1]
,我们可以知道,当我们要计算第 i 行的值时,除了会用到第 i - 1
行外,其他第 1
至 第 i-2
行的值我们都是不需要用到的,因为计算某一行的时候,依赖的是左一结果和上一结果,所以我们存一行的记录,只要这一行包含当前元素的左一结果和上一结果即可。我们只需要用一个一维的 dp[]
来保存一行的历史记录就可以了。然后在计算的过程中,不断着更新 dp[]
的值。
所以在dp[n]
这个数组里面,dp[0]...dp[j-1]
是本一行的,dp[j]...dp[n]
是上一行。
代码
class Solution {
public:
int uniquePaths(int m, int n) {
if (m <= 0 || n <= 0)
{
return 0;
}
if (m == 1 || n == 1)
{
return 1;
}
vector<int> dp(n);
// 初始化
for (int i = 0; i < n; i++)
{
dp[i] = 1;
}
// 推导出 dp[m-1][n-1]
for (int i = 1; i < m; i++)
{
// 第 i 行第 0 列的初始值
dp[0] = 1;
for (int j = 1; j < n; j++)
{
dp[j] = dp[j - 1] + dp[j];
}
}
return dp[n - 1];
}
};