文章出处:极客时间《数据结构和算法之美》-作者:王争。该系列文章是本人的学习笔记。

题目

假设我们有一个 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]==0∣∣memo[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 零钱兑换。

动态规划——矩阵中的最短路径长度相关推荐

  1. 动态规划-矩阵中的最短路径

    一.牛客 NC59 矩阵的最小路径和 给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和. 数 ...

  2. 数据结构——图-最短路径长度中最大的一个

    #include<stdio.h> #include<string.h> #define INF 32767 #define MAXVEX 30 int dist[MAXVEX ...

  3. 【组合数学+动态规划】在如下8*6的矩阵中,请计算从A移动到B一共有____种走法。要求每次只能向上或向右移动一格,并且不能经过P。...

    在如下8*6的矩阵中,请计算从A移动到B一共有__种走法.要求每次只能向上或向右移动一格,并且不能经过P. A:456  B:492  C:568  D:626  E:680  F:702 解析:  ...

  4. 矩阵中的最长递增路径

    矩阵中的最长递增路径 文章目录 矩阵中的最长递增路径 一.问题描述 **方法一:朴素的深度优先搜索 [超时]** **方法二:记忆化深度优先搜索 [通过]** **方法三:动态规划** **方法四:广 ...

  5. 【vivo2021届秋季校招编程题】【java】广度优先搜索(BFS)/深度优先搜索(DFS)找最短路径长度

    vivo2021届秋季校招编程题 图中 找两点间的最短路径长度 广度搜索bfs/深度搜索dfs vivo游戏中心的运营小伙伴最近接到一款新游戏的上架申请,为了保障用户体验,运营同学将按运营流程和规范对 ...

  6. 01二维矩阵中最大全为1的正方形maxSquare——经典DP问题(二维)

    在一个二维01矩阵中找到全为1的最大正方形 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 以矩阵中每一个点作为正方形右下角点来处理,而以该点为右下角点的最大边长最多比 ...

  7. 77. Leetcode 1439. 有序矩阵中的第 k 个最小数组和 (堆-技巧二-多路归并)

    技巧二 - 多路归并其实这个技巧,叫做多指针优化可能会更合适,只不过这个名字实在太过朴素且容易和双指 针什么的混淆,因此我给 ta 起了个别致的名字 - 多路归并.多路体现在:有多条候选路线.代码上, ...

  8. 算法练习day20——190411(重建二叉树、斐波那契数列、跳台阶、矩形覆盖、变态跳台阶、旋转数组的最小数字、矩阵中的路径)

    1.重建二叉树 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. preorder = [3,9,20,15,7].inorder = [9 ...

  9. 【算法】广度遍历算法的应用 求出距离顶点v0的最短路径长度为最长的一个顶点,图结构的bfs生成树及其双亲表示形式

    例: 求出距离顶点v0的最短路径长度为最长的一个顶点,并要求尽可能节省时间 分析: 用bfs算法(利用bfs算法的层次特性): 从v0出发进行广度遍历时, 最后一层的顶点距离v0的最短路径长度最长.因 ...

最新文章

  1. SpringMVC - 1.DispatcherServlet
  2. 洛谷 P2573 [SCOI2012]滑雪
  3. 2009年5月软件设计师考前预测试题及考点解析
  4. 图像形状特征提取c语言,OpenCV_局部图像特征的提取与匹配_源代码
  5. 再谈初学者关心的ssh应用方方面面
  6. Ubuntu 学习系列-安装Flash播放器
  7. java 指定格式的date_指定格式的日期字符串转化成java.util.Date类型日期对象
  8. 网络蚂蚁(netants) v1.25 国际版 bt
  9. python爬虫当当网图书信息_利用python爬虫可视化分析当当网的图书数据!
  10. 国外调查问卷怎么做?
  11. html5的video播放腾讯视频播放器,html 5中使用video元素制作一个影片播放器
  12. tnl 的 masterServer, client server 架构学习笔记
  13. 【观察】并购魔方安全,联软科技再启新征程
  14. 写给女儿高中编程课老师的一封信
  15. 巴比特 | 元宇宙每日必读:继续加码!韩国政府将投资179亿韩元扶持本国元宇宙企业的内容开发及海外扩张...
  16. android 微信授权获取用户个人信息
  17. 这些隐藏功能你知道吗
  18. BeautifulSoup 与 Xpath
  19. 输入相应的数打印三角形
  20. P1462 通往奥格瑞玛的道路

热门文章

  1. SQLServer学习笔记系列4
  2. 笛卡尔乘积算法的体现
  3. 微信新的用户信息接口wx.getUserProfile,返回信息解密失败
  4. phpsduty环境下,使用composer安装报错
  5. Struts2的通配符配置方式
  6. mac 查看端口并终结
  7. WhqDatabase 我自己用C#开发的列式数据库
  8. 糖豆人服务器要维护多久,糖豆人刚上四天,紧急维护9小时后又延长,玩家:从中午到现在了...
  9. linux的yum详解,Linux之YUM 详解
  10. mysql 4 中文模糊查询_解决MySQL中文模糊查询问题