开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第7天,点击查看活动详情。
最小路径和
描述
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明: 每次只能向下或者向右移动一步。
难度
中等
示例
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
思路
该问题可以看成求原数组中每个点的最小路径,每一个点的最小路径都依赖前面点的最小路径,所以可采用动态规划来解。
因每次只能向下或者向右移动一步,第一列所有点的最小路径都等于它上边的最小路径加上该点的值,第一行的所有点的最小路径都等于它左边的最小路径加上该点的值,而其它点的最小路径等于它上边和左边最小路径中最小的值加上该点的值。
创建一个二维数组 pathSums,和原始数组 grid 大小相同,pathSum[i][j] 表示从起点到 (i, j) 位置的最小路径和。其中 pathSum[0][0] = grid[0][0],第一列,第一行和其它元素通过以下状态转移方程计算:
- i > 0, j = 0 时, pathSums[i][0] = pathSums[i - 1][0] + grid[i][0]
- i = 0, j > 0 时, pathSums[0][j] = pathSums[0][j - 1] + grid[0][j]
- i > 0, j > 0 时, pathSums[i][j] = min(pathSum[i - 1][j], pathSum[i][j - 1]) + pathSums[i][j]
最后一个点 (i, j) 最小路径为 pathSums[i][j]。
代码
Rust
pub struct Solution {}
impl Solution {
pub fn min_path_sum(grid: Vec<Vec<i32>>) -> i32 {
if grid.len() == 0 {
return 0;
}
let (row, col) = (grid.len(), grid[0].len());
// 用来保存原数组对应点的最小路径
let mut path_sums = vec![vec![0; col]; row];
// 起始点 0,0
let (start_x, start_y) = (0, 0);
path_sums[start_y][start_x] = grid[start_y][start_x];
// 第一列所有点的最小路径
for i in start_y + 1..row {
path_sums[i][0] = path_sums[i - 1][0] + grid[i][0];
}
// 第一行所有点的最小路径
for j in start_x + 1..col {
path_sums[0][j] = path_sums[0][j - 1] + grid[0][j];
}
// 其它点的最小路径,路径等于上边和左边中最小的值加上当前点的值
for i in start_y + 1..row {
for j in start_x + 1..col {
path_sums[i][j] =
std::cmp::min(path_sums[i - 1][j], path_sums[i][j - 1]) + grid[i][j];
}
}
// 返回最后一个点的最小路径
path_sums[row - 1][col - 1]
}
}
#[test]
fn test_min_path_sum() {
let grid = vec![vec![1, 3, 1], vec![1, 5, 1], vec![4, 2, 1]];
let sum = Solution::min_path_sum(grid);
println!("{}", sum);
}
运行结果:
7
Go
func minPathSum(grid [][]int) int {
if len(grid) == 0 {
return 0
}
row, col := len(grid), len(grid[0])
// 用来保存原数组对应点的最小路径
pathSums := make([][]int, row)
for i := 0; i < len(pathSums); i++ {
pathSums[i] = make([]int, col)
}
// 起始点 0,0
startX, startY := 0, 0
pathSums[startY][startX] = grid[startY][startX]
// 第一列所有点的最小路径
for i := startY + 1; i < row; i++ {
pathSums[i][0] = pathSums[i - 1][0] + grid[i][0]
}
// 第一行所有点的最小路径
for j := startX + 1; j < col; j++ {
pathSums[0][j] = pathSums[0][j - 1] + grid[0][j]
}
// 其它点的最小路径,路径等于上边和左边中最小的值加上当前点的值
for i := startY + 1; i < row; i++ {
for j := startX + 1; j < col; j++ {
pathSums[i][j] = int(math.Min(float64(pathSums[i - 1][j]),
float64(pathSums[i][j - 1]))) + grid[i][j]
}
}
// 返回最后一个点的最小路径
return pathSums[row - 1][col - 1]
}
func TestMinPathSum(t *testing.T) {
grid := [][]int {
{1, 3, 1},
{1, 5, 1},
{4, 2, 1},
}
sum := minPathSum(grid)
t.Log(sum)
}
运行结果:
7
Java
public class Main {
public static int minPathSum(int[][] grid) {
if (grid.length == 0) {
return 0;
}
int row = grid.length, col = grid[0].length;
// 用来保存原数组对应点的最小路径
int[][] pathSums = new int[row][col];
// 起始点 0,0
int startX = 0, startY = 0;
pathSums[startY][startX] = grid[startY][startX];
// 第一列所有点的最小路径
for (int i = startY + 1; i < row; i++) {
pathSums[i][0] = pathSums[i - 1][0] + grid[i][0];
}
// 第一行所有点的最小路径
for (int j = startX + 1; j < col; j++) {
pathSums[0][j] = pathSums[0][j - 1] + grid[0][j];
}
// 其它点的最小路径,路径等于上边和左边中最小的值加上当前点的值
for (int i = startY + 1; i < row; i++) {
for (int j = startX + 1; j < col; j++) {
pathSums[i][j] = Math.min(pathSums[i - 1][j], pathSums[i][j - 1]) + grid[i][j];
}
}
// 返回最后一个点的最小路径
return pathSums[row - 1][col - 1];
}
public static void main(String[] args) {
int[][] grid = new int[][] {
{1, 3, 1},
{1, 5, 1},
{4, 2, 1}
};
int sum = minPathSum(grid);
System.out.println(sum);
}
}
运行结果:
7