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];
}
};