最小路径和:leetcode-64

164 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

最小路径和

给定一个包含非负整数的 m * n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明: 每次只能向下或者向右移动一步。

示例:

输入:

[
  [1, 3, 1],
  [1, 5, 1],
  [4, 2, 1]
]

输出:7

解释:因为路径 1 -> 3 -> 1 -> 1 -> 1 的总和最小

题解:

我们在遇到这样的统计可行路径的数量,或者求最小路径的时候,比较容易想到的两种做法:一个是搜索,另一个是动态规划。

而搜索的做法,仅仅在数据规模比较小的时候才考虑使用,所以在这里,我们尝试采用动态规划来解决这个问题。

动态规划法

动态规划算法,我们需要关注以下两点:

  1. 状态的设置:在本题中,由于要求最小路径和,我们可以令 dp[i][j] 代表从 (0, 0) 走到 (i, j) 的最小路径和
  2. 状态转移方程:我们考虑如何来求出 dp[i][j]。由于每次只能往右或者往下走,所以只能从 (i-1, j)、(i, j-1) 到达 (i, j),所以我们在这两者中取最小,然后在加上 (i, j) 位置的数即可

代码实现:

var minPathSum = function(grid) {
  if(grid.length === 0) return 0;
  let dp = []; // 保存从(0,0)到当前位置的权重,是一个二维数组
  let m = grid.length; // 行
  let n = grid[0].length; // 列
  // 初始化二维数组
  for(let i=0; i<m; i++) dp[i] = [];
  
  for(let i=0; i<m; i++) {
    for(let j=0; j<n; j++) {
      // 遍历每一个位置
      if(i === 0 && j === 0) { // (0, 0) 的权重为自身的数
        dp[i][j] = grid[i][j];
        continue;
      }
      if(i === 0 && j !== 0) {
        // 当前位置是第一行,但不是第一列:上一次移动只能是向右移动(因为已经是首行了:i为0)
        dp[i][j] = dp[i][j-1] + grid[i][j]; // 拿上一次的结果 + 当前位置的权重,就是当前位置dp的值了
        continue;
      }
      if(j === 0 && i !== 0) {
        // 和上面同理,当前位置是第一列,但不是第一行:只能是从上往下的移动
        dp[i][j] = dp[i-1][j] + grid[i][j];
        continue;
      }
      // 可能是从左到右,也可能是从上到下。比较左方和上方,拿到最小的那个
      dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
    }
  }
  // 返回右下角的数
  return dp[m-1][n-1];
}