矩阵的最小路径和(java求解)

本文介绍如何使用动态规划解决寻找矩阵中从左上角到右下角的最小路径和问题,包括完整算法实现及空间优化方法。

【题目】

给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加在一起就是路径和,返回所有的路径中最小的路径和。

【举例】

给定m如下:
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
路径1,3,1,0,6,1,0是所有路径中路径和最下的,返回12.

【解答】

经典动态规划问题。假设矩阵m的大小为M*N,行数为M,列数为N。先生成大小和m一样的矩阵dp,dp[]i[j]的值表示从左上角(0,0)位置走到(i,j)位置的最小路径和。对m的第一行的所有位置,即(0,j),从(0,0)到(0,j)位置只能向右走,所以从(0,0)到(0,j)位置的路径和就是m[0][0…j]这些值累加的结果。同理,从(0,0)到(i,0)位置的路径和就是m[0…i][0]这些值累加的结果。
从而得到dp[i][j]=min{dp[i-1][j],dp[i][j-1]}+m[i][j]
(0,0) . . . .
. .
. .
. (i,j-1)
. . .(i-1,j) (i,j)
由例题可得
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
dp的第一行和第一列为
1 4 9 18
9
14
22
所以最终生成的dp矩阵如下:
1 4 9 18
9 5 8 12
14 5 11 12
22 13 15 12
除第一行和第一列外,每个位置都考虑从左边到达自己的路径和更小还是从上面到达自己的路径和最小。所以最右下角的值就是整个问题的答案

public int minPathSum1(in[][] m){
  if(m == null || m.length == 0 || m[0] == null || m[0].length == 0){
    return 0;
  }
  int row = m.length;
  int col = m[0].length;
  int[][] dp = new int[row][col];
  dp[0][0] = m[0][0];
  for (int i = 1; i<row; i++){
    dp[i][0] = dp[i-1][0]+m[i][0];
  }
  for (int j = 1; j<col; j++){
    dp[0][j] = dp[0][j-1]+m[0][j];
  }
  for (int i = 1; i<row; i++){
      for (int j = 1; j<col; j++)
    dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+m[i][j];
  }
  return dp[row-1][col-1];
}

所以时间复杂度是O(M*N)
所以空间复杂度是O(M*N)

空间压缩

动态规划经过空间压缩后的方法。时间复杂度依旧为O(M * N),但额外空间复杂度可以减小至O(min{M,N}),,也就是不使用大小为M *N的dp矩阵,改为大小为min{M,N}的arr矩阵。
1、生成长度为4的数组arr,初始时arr=[0,0,0,0].我们知道从(0.0) 位置到达m中第一行的每个位置,最小路径和就是从(0.0) 位置的值开始依次累加的结果,所以依次把arr设置为arr[1,4.9,18],此时arr[j]的值代表从(0,0) 位置达到(0,j)位置的最小路径和。
2、步骤1中arr[j]的值代表从(0,0) 位置达到(0,j) 位置的最小路径和,在这一步中想把
m的值更新成从 (0,0)位置达到(1,j) 位置的最小路径和。首先来看arr[0],更新之前arr[0]
的值代表(0,0)位置到达(0,0)位置的最小路径和(dp[0][0]), 如果想把arr[0]更新成从(0,0)
位置达到(1,0)位置的最小路径和(dp[1][0]), 令arr[0]=arr[0]+m[1][0]=9即可。然后来看arr[1],更新之前arr[1]的值代表(0,0)位置到达(0,1)位置的最小路径和(dp[0][1]), 更新之后想让arr[1]代表(0,0)位置到达(1,1) 位置的最小路径和(dp[1][1])。根据动态规划的求解过程,到达(1,1)位置有两种选择,一种是从(1,0)位置到达(1.1) 位置(dp[1][0]+m[1][1]),另一种是从(0,1)位置到达(1,1)位置(dp[0][1]+m[1][1])应该选择路径和最小的那个。 此时arr[0]的值已经更新成dp[1][0],) arr1目前还没有更新,所以,arr1还是dp[0][1],arr[1] = min{arr[0],arr[1]}+m[1][1] = 5。更新之后,arr[1]的值变为dpl[1][1]的值。同理,arr[2] = min{arr[2],arr[1]}+m[1][2], …最终arr可以更新成[9,5,8,12]。
3、重复步骤2的更新过程,直到arr彻度变成dp矩阵的最后一行。整个过程其实就是不断滚动更新arr数组,让arr依次变成dp矩阵每一行的值,最终变成dp矩阵最后一行的值。

public int minPathSum2(in[][] m){
  if(m == null || m.length == 0 || m[0] == null || m[0].length == 0){
    return 0;
  }
  int more = Math.max(m.length,m[0].length);//行数与列数较大的那个为more
  int less = Math.min(m.length,m[0].length);//行数与列数较小的为less
  boolean rowmore = more ==m.length;//行数是不是等于或者大于列数
  int[] arr  = new int[less];//辅助数组的长度仅为行数与列数中最小值
  arr[0] = m[0][0];
  for (int i = 1; i<less; i++){
    arr[i] = arr[i-1]+(rowmore ? m[0][i] : m[i][0]);
  }
  for (int i = 1; i<more; i++){
    arr[0] = arr[i-1]+(rowmore ? m[i][0] : m[0][i]);
    for (int j = 1; j<more; j++)
      arr[j] = Math.min(arr[j-1],arr[j])+(rowmore ? m[i][j] : m[j][i]);
  }
  return arr[less-1];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值