日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划——矩阵中的最短路径长度

發布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划——矩阵中的最短路径长度 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章出處:極客時間《數據結構和算法之美》-作者:王爭。該系列文章是本人的學習筆記。

題目

假設我們有一個 n 乘以 n 的矩陣 w[n][n]。矩陣存儲的都是正整數。棋子起始位置在左上角,終止位置在右下角。我們將棋子從左上角移動到右下角。每次只能向右或者向下移動一位。從左上角到右下角,會有很多不同的路徑可以走。我們把每條路徑經過的數字加起來看作路徑的長度。那從左上角移動到右下角的最短路徑長度是多少呢?

回溯法

從 (0, 0) 走到 (n-1, n-1),每一步都有向下或者向右2種選擇方式。當走到 (n-1, n-1)停止。
寫回溯代碼中,遞歸函數f的參數,僅僅與狀態有關,與狀態無關的變量(例如n、m、grid)都作為類的實例變量保存起來。

public class MatrixShortestPath {private int n;private int m;private int min;private int[][] grid;public int minPathSum(int[][] grid) {if(grid==null || grid.length==0) return 0;n = grid.length;m = grid[0].length;this.grid = grid;min = Integer.MAX_VALUE;f(0,0,0);return min;}private void f(int i,int j,int currentSum){//System.out.println(i+" " +j);if(i==n-1 && j>=m || i>=n && j==m-1){min = Math.min(min,currentSum);return;}if(i>=n || j>=m) return;f(i,j+1,currentSum+grid[i][j]);f(i+1,j,currentSum+grid[i][j]);} }

我們根據上面這個特殊的例子,把回溯求解問題的遞歸樹畫出來。

遞歸樹的每一個節點表示一個狀態,用(i,j,currentPathSum),表示當前將要處理第i行第j列的數據,在處理之前已經走過的路徑長度是currentPathSum。

在遞歸樹上能看到雖然(i,j,currentPathSum)重復的不存在,但是這道題目要求的是到達(n-1,n-1)的時候最短路徑長度,所以在處理的時候只需要知道到達(i,j)的時候最短的路徑長度是多少,其余節點就無需向下擴散了。例如f(1,2,9),f(1,2,5),f(1,2,3)。那只需要保留f(1,2,3),f(1,2,9),f(1,2,5)無需向下擴散。因為達到(1,2)節點之后,仍然是向右、向下走,與currentPathSum等于5、9,3都無關。這樣就保證了節點不會指數級增長。

對遞歸樹剪枝——加緩存

int[][] memo,memo[i][j]=currentSum。當調用f(1,2,3)的時候,如果memo[1][2]值為0 ,那就設置memo[1][2]=3,向下計算。當遇到f(1,2,9)的時候,發現memo[1][2]=3,而9>3,則不再計算。
代碼的變化就是加了判斷:memo[i][j]==0∣∣memo[i][j]>currentSummemo[i][j]==0 || memo[i][j]>currentSummemo[i][j]==0memo[i][j]>currentSum

public class MatrixShortestPath {private int n;private int m;private int min;private int[][] grid;private int[][] memo;public int minPathSum(int[][] grid) {if(grid==null || grid.length==0) return 0;n = grid.length;m = grid[0].length;this.grid = grid;min = Integer.MAX_VALUE;memo = new int[n][m];f(0,0,0);return min;}private void f(int i,int j,int currentSum){//System.out.println(i+" " +j+" "+currentSum);if(i==n-1 && j>=m || i>=n && j==m-1){min = Math.min(min,currentSum);return;}if(i>=n || j>=m) return;if(memo[i][j]==0 || memo[i][j]>currentSum){memo[i][j] = currentSum;f(i,j+1,currentSum+grid[i][j]);f(i+1,j,currentSum+grid[i][j]);}} }

對遞歸樹剪枝——動態規劃

狀態轉移表

接下來我們按照這種方式,計算狀態轉移表。我們畫出一個二維狀態表,表中的行、列表示棋子所在的位置,表中的數值表示從起點到這個位置的最短路徑。注意:這里狀態表的值的含義與遞歸樹中的值的含義發生了變化。我們看到在遞歸樹中到達最后一個位置,還需要currentPathSum+matrix[i][j]。但在表中是不需要的。
如果把表定義為int[][] dp ,dp[i][j]=到達(i,j)位置的最短路徑。我們想要的返回值就是dp[n-1][m-1]。
我們按照決策過程,通過不斷狀態遞推演進,將狀態表填好。為了方便代碼實現,我們按行來進行依次填充。

初始化這一步是很重要的。
對dp[0][0]=grid[0][0]。
對于第0行,大于0的列,只能從左側的位置轉移到當前位置:dp[0][j-1]+grid[0][j]。
對于第0列,只能從上面的位置轉移到當前位置:dp[i][0]=dp[i-1][0]+grid[i][0]。

代碼:

public int minDistDP(int[][] matrix ,int n){int[][] states = new int[n][n];//第一行for(int j=0;j<n;j++){if(j==0){states[0][j] = matrix[0][j];}else{states[0][j] = states[0][j-1]+matrix[0][j];}}//第一列for(int i=1;i<n;i++){states[i][0] = states[i-1][0]+matrix[i][0];}for(int i=1;i<n;i++){for(int j=1;j<n;j++){states[i][j] = Math.min(states[i-1][j],states[i][j-1])+matrix[i][j];}}return states[n-1][n-1];}

狀態轉移方程

定義int[][] dp ,dp[i][j]=到達(i,j)位置的最短路徑。
我們知道可以從(i-1,j)或者(i,j-1)兩個狀態到達(i,j)。
從 (i-1,j)到達(i,j),路徑和需要增加grid[i][j],也就是說dp[i][j]=dp[i-1][j]+grid[i][j]。
從(i,j-1)到達(i,j),路徑和需要增加grid[i][j],也就是說dp[i][j]=dp[i][j-1]+grid[i][j]。
那么轉移方程就是

dp[i][j] = min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]) =min(dp[i-1][j],dp[i][j-1])+grid[i][j]

初始化:

對dp[0][0]=grid[0][0]。 對于第0行,大于0的列,只能從左側的位置轉移到當前位置:dp[0][j-1]+grid[0][j]。 對于第0列,只能從上面的位置轉移到當前位置:dp[i][0]=dp[i-1][0]+grid[i][0]。

實現代碼:

public int minPathSum(int[][] grid) {if(grid==null || grid.length==0) return 0;int n = grid.length;int m = grid[0].length; int[][] dp = new int[n][m];for(int j=0;j<m;j++){dp[0][j] = (j==0?grid[0][0]:dp[0][j-1]+grid[0][j]);}for(int i=1;i<n;i++){dp[i][0] = dp[i-1][0]+grid[i][0];}for(int i=1;i<n;i++){for(int j=1;j<m;j++){dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];}}return dp[n-1][m-1];}

類似題目

可以用相同的思路處理 LeetCode 322 零錢兌換。

總結

以上是生活随笔為你收集整理的动态规划——矩阵中的最短路径长度的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。