携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
给定一个包含非负整数的 m * n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明: 每次只能向下或者向右移动一步。
示例:
输入:
[
[1, 3, 1],
[1, 5, 1],
[4, 2, 1]
]
输出:7
解释:因为路径 1 -> 3 -> 1 -> 1 -> 1 的总和最小
题解:
我们在遇到这样的统计可行路径的数量,或者求最小路径的时候,比较容易想到的两种做法:一个是搜索,另一个是动态规划。
而搜索的做法,仅仅在数据规模比较小的时候才考虑使用,所以在这里,我们尝试采用动态规划来解决这个问题。
动态规划法
动态规划算法,我们需要关注以下两点:
- 状态的设置:在本题中,由于要求最小路径和,我们可以令
dp[i][j]代表从(0, 0)走到(i, j)的最小路径和 - 状态转移方程:我们考虑如何来求出
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];
}