题目描述
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明: 每次只能向下或者向右移动一步。
示例:
输入: [ [1,3,1], [1,5,1], [4,2,1] ]
输出: 7
解释: 因为路径 1→3→1→1→1的总和最小。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路1——动态规划
比较简单的动态规划题目,由于只能向下或向右移动,终点(m, n)的上一个位置只能是(m-1, n)或(m, n-1),问题转化为求从左上角到(m-1, n)和(m, n-1)的最短距离。因此,左上角到某点(i. j)的最短路径为左上角到(m-1, n)和(m, n-1)的最短路径中较小的值加上(i, j)的值,可以得到动态规划的状态转移方程为dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]。
左上角到(0, j)的只能从(0, 0)向右移动到达,因此最短路径dp[0][j] = dp[0][j-1] + grid[0][j],同理可得dp[i][0] = dp[i-1][0] + grid[i][0],这是动态规划的初始状态。将数组dp填充完毕后,即可得到最短路径dp[m-1][n-1]。
核心算法
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
// 表示到第i行第j列的最小路径
int dp[m][n];
// 动态规划初始状态
dp[0][0] = grid[0][0];
for(int i = 1; i < n; i++){
dp[0][i] = dp[0][i - 1] + grid[0][i];
}
for(int i = 1; i < m; i++){
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
// 状态转移方程
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
dp[i][j] = min(dp[i - 1][j] + grid[i][j], dp[i][j - 1] + grid[i][j]);
}
}
return dp[m - 1][n - 1];
}
};
解题思路2——DFS
本题也可采用深度优先搜索的方法解决,与动态规划从进到远一步一步计算最短路径相反,DFS以递归的方式先计算(m-1, n)和(m, n-1)到终点(m, n)的最短路径,再一层层向前反推。(i, j)到终点的最短路径计算公式为memo[i][j] = min(memo[i+1][j], memo[i][j+1]) + grid[i][j]。
核心算法
class Solution {
private:
int m, n;
int memo[100][100];
public:
int dfs_minPathSum(vector<vector<int>>& grid) {
m = grid.size(), n = grid[0].size();
// 全部初始化为-1
for (int i = 0; i < m; i++) {
memset(memo[i], -1, sizeof(int) * n);
}
return dfs(grid, 0, 0);
}
// 深度优先搜索,返回第r行第c列到终点的最短路径
int dfs(vector<vector<int>>& grid, int r, int c){
// 越界则认为距离无穷大
if (r < 0 || r >= m || c < 0 || c >= n)
return 1000000;
// 若已经计算得第r行第c列到终点的最短路径,直接返回
if(memo[r][c] != -1)
return memo[r][c];
// 终点到终点的最短路径
if(r == m - 1 && c == n - 1){
memo[r][c] = grid[m - 1][n - 1];
return memo[r][c];
}
// 递归,当前最短路径为右侧或下侧位置到终点的最短路径(取较小值)加该点的值
int right = dfs(grid, r, c + 1);
int down = dfs(grid, r + 1, c);
memo[r][c] = min(right, down) + grid[r][c];
return memo[r][c];
}
};