leetcode 64. 最小路径和 -- javascript DP

757 阅读2分钟

题目描述:

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

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

image.png

题解:

动态规划(Dynamic Programming, DP)

  • 动态规划只能应用于有最优 子结构的问题。最优子结构的意思是局部最优解能决定全局最优解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。

  • 简单地说,问题能够分解成子问题来解决

  • 通俗一点来讲,动态规划和其它遍历算法(如深/广度优先搜索)都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存子问题的解,避免重复计算

  • 解决动态规划问题的关键是找到状态转移方程,这样我们可以通计算和储存子问题的解来求解最终问题

  • 同时,我们也可以对动态规划进行空间压缩,起到节省空间消耗的效果。

  • 在一些情况下,动态规划可以看成是带有状态记录(memoization)的优先搜索

  • 动态规划是自下而上的,即先解决子问题,再解决父问题;

  • 而用带有状态记录的优先搜索自上而下的,即从父问题搜索到子问题,若重复搜索到同一个子问题则进行状态记录,防止重复计算。

  • 如果题目需求的是最终状态,那么使用动态搜索比较方便;

  • 如果题目需要输出所有的路径,那么使用带有状态记录的优先搜索会比较方便。

回到本题目

dp[i][j] 表示从左上角开始到 (i, j) 位置的最

优路径的数字和。因为每次只能向下或者向右移动,我们可以很容易得到状态转移方程 dp[i][j] =

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

var minPathSum = function(grid) {
   let m = grid.length, n = grid[0].length;
   let dp = Array.from({length: m}, ()=> new Array(n).fill(0));
   for(let i = 0; i < m; i++) {
       dp[i][0] = i === 0 ? grid[i][0] : dp[i-1][0] + grid[i][0];
   }
   for(let j = 0; j < n; j++) {
       dp[0][j] = j === 0 ? grid[0][j] : dp[0][j - 1] + grid[0][j];
   }
   for(let i = 1; i < m; i++){
      for(let j = 1; j < n; j ++) {
          dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
      }
   }
   return dp[m-1][n-1];
};

压缩空间

var minPathSum = function(grid) {
   let m = grid.length, n = grid[0].length;
   let dp = new Array(n).fill(0);
   for(let i = 0; i < m; i++){
      for(let j = 0; j < n; j ++) {
          if(i== 0 && j== 0) {
              dp[j] = grid[i][j]
          }else if(i == 0) {
              dp[j] = dp[j-1] + grid[i][j]
          }else if(j === 0) {
              dp[j] = dp[j] + grid[i][j]
          }else{
              dp[j] = Math.min(dp[j], dp[j-1]) + grid[i][j]; 
          }
           
      }
   }
   return dp[n-1];
};