1. 程式人生 > 實用技巧 >LeetCode 陣列:62. 不同路徑(動態規劃 帶記憶的遞迴)

LeetCode 陣列:62. 不同路徑(動態規劃 帶記憶的遞迴)

,接下來

在沒有接觸過動態規劃之前,是這樣思考這個題的。

首先想到的就是遞迴中青蛙跳臺階的問題,自然想到遞迴。就使用原函式 intuniquePaths(intm,intn)

  • 函式的作用,遵循規則的情況下,有多少中途徑到達座標為(m-1,n-1)的位置
  • 遞迴終止條件:原點(m=n=1),函式返回1,如果m==0||n==0也是返回1(只有一種)
  • 遞迴關係:uniquePaths(m,n)=uniquePaths(m-1,n)+uniquePaths(m,n-1)

程式碼完成如下:

class Solution {
    public int uniquePaths(int m, int
n) { if(m==1||n==1) {return 1;} return uniquePaths(m-1,n)+uniquePaths(m,n-1); } }

然而超時了。。。

遞迴有重複計算的問題,在很多格子中,明明曾經已經計算過結果,但是碰到仍然向後繼續遞迴了:

加一個數組,用於儲存已經計算的結果

class Solution {
    int[][] dp = new int[100][100];
    public int uniquePaths(int m, int n) {
        return cycle(m-1,n-1);
    }
    
public int cycle(int i,int j){ if(i == 0 || j == 0) return 1; if(dp[i][j] != 0) return dp[i][j]; dp[i][j] = cycle(i-1,j)+cycle(i,j-1); return dp[i][j]; } }

通過接下來看動態規劃的演算法。

動態規劃是正著推,而且有一個用於存放結果的一維/二維陣列,大部分問題是二維的。

首先需要確認該問題是否符合動態規劃解題要求
動態規劃適合解決的問題模型符合"一個模型三個特性"
一個模型可以概括為:多階段決策最優解模型; 本題目第一行是已經定好了都是1,
在第二行的時候,每個方格的值都是該方格左邊的格和該方格上邊的格的總和,以此類推,滿足.
  • 特性1:最優子結構;每個階段的狀態或值都是通過前面階段的狀態或值推匯出來的,滿足.
  • 特性2:無後效性;每個階段的狀態值一旦確定之後,是不受後面階段狀態值所影響的,滿足.
  • 特性3:重複子問題;從遞迴解法中就能看出來有重複子問題的計算,滿足.
接下來,該分析如何套用動態規劃解題步驟
首先,定義二維陣列儲存路徑條數,按照遞迴中分析得出第一行和第一列都是1,所以二維陣列直接定義成如此
然後分階段階段,每一橫行的處理就是一個階段,通過上一行就能推匯出下一行的狀態值
在每一個階段中,同一行中的方格,是其左邊方格的值加上一行方格的總和
最後返回二維陣列最後一個元素的值即可.
class Solution {
    public int uniquePaths(int m, int n) {
        int [][] dp = new int[m+1][n+1];
        //
        for(int i = 1;i<m+1;i++){
            //
            for(int j = 1;j<n+1;j++){
                if(i == 1 && j == 1){
                    dp[1][1] = 1;
                }else{
                    //狀態轉移方程
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
            }
        }
        return dp[m][n];
    }
}

這樣的時間複雜度和空間複雜度都是om*n

有優化的辦法,把空間複雜度優化為O2n或者On

優化一:將一個表優化成了表中我們需要的兩行

優化二:在優化一的基礎上,將兩行優化成了我們需要的當前行,因為cur未更新前儲存的結果是上一行的結果

另外,這道題其實直接排列組合就能做: