64. 最小路径和

153 阅读3分钟

题目描述

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明: 每次只能向下或者向右移动一步。

minpath.jpg

示例

示例 1:

输入: grid = [[1,3,1],[1,5,1],[4,2,1]]
输出: 7
解释: 因为路径 13111 的总和最小。

条件约束

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200

题目描述重点划词

  • 左上角
  • 右下角
  • 数字总和为最小

思考

从题目描述的关键词中不难看出这就是一个动态规划的题目,没有一点技巧可言。从左上角到右下角每走一步都要挑选最优的路径,局部最优+其他最优 == 全局最优影响到达 [i][j] 的花费只有两个点有关,分别是点上方和点左边,ok 动态规划转移方程如喝水一样简单

dp[i][j] = cost[i][j] + max(dp[i-1][j], dp[i][j-1])

其中有个问题边界怎么搞,因为 [0][j] 没有上方的的点 [i][0] 没有左边的点,思考中...嗯好像很简单,到达它们这些点的路径路数只有一条最上方的点只能从左边过来,最左边的点只能从上方过来,所以 dp[0][j] == cost[0][j] + dp[0][j-1]、dp[i][0] == cost[i][0] + dp[i-1][0]

下面是AreaZer同学给出的示例代码

func minPathSum(grid [][]int) int {
   m, n := len(grid), len(grid[0])
   dp := make([][]int, m)
   for i := range dp {
      dp[i] = make([]int, n)
   }
   dp[0][0] = grid[0][0] // 右上角
   // 最左边一列
   for i := 1; i < m; i++ {
      dp[i][0] = dp[i-1][0] + grid[i][0]
   }
   // 最上面那一行
   for j := 1; j < n; j++ {
      dp[0][j] = dp[0][j-1] + grid[0][j]
   }
   for i := 1; i < m; i++ {
      for j := 1; j < n; j++ {
         dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
      }
   }
   return dp[m-1][n-1]
}

image.png 啊?这... 内存占用被甩一大截,肯定我还有做的不好的地方,那我想想,o O 哦!其实我可以不用开辟 dp 额外空间,从 for 循环看 grid[i][j] 都只是利用了一次而且数组大小和我的 dp 一模一样,我是不是可以直接使用 grid,直接在基础上改,ok,我先试试

func minPathSum2(grid [][]int) int {
   m, n := len(grid), len(grid[0])
   for i := 0; i < m; i++ {
      for j := 0; j < n; j++ {
         if i+j == 0 { // 右上角
            continue
         }
         if i == 0 { // 最上面那一行
            grid[i][j] += grid[i][j-1]
            continue
         }
         if j == 0 { // 最左边一列
            grid[i][j] += grid[i-1][j]
            continue
         }
         grid[i][j] += min(grid[i-1][j], grid[i][j-1])
      }
   }
   return grid[m-1][n-1]
}

image.png 啊?这用时怎么还边长了,这...难道是我的 if 太多了优雅的代码变成了石(si)山,if 语句大多数都可以用 switch case 去优化,好吧那我来试试

func minPathSum3(grid [][]int) int {
   m, n := len(grid), len(grid[0])
   for i := 0; i < m; i++ {
      for j := 0; j < n; j++ {
         switch i + j {
         case 0: // i == 0 && j == 0
            continue
         case i: // j == 0
            grid[i][j] += grid[i-1][j]
         case j: // i == 0
            grid[i][j] += grid[i][j-1]
         default:
            grid[i][j] += min(grid[i-1][j], grid[i][j-1])
         }
      }
   }
   return grid[m-1][n-1]
}

image.png

我心满意足了,你们满意吗?

题目来源 64. 最小路径和