leetcode64:最小路径和

题目链接

leetcode

题目描述

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

C++ 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <iostream>
#include <vector>
using namespace std;

void printArray(vector<vector<int>>& arr) {
for (auto& row : arr) {
for (int num : row) {
cout << num << " ";
}
cout << endl;
}
}

/*
动态规划

状态表示:
f(i, j) 表示从起点到(i, j)的路径和,取 min
状态计算:
向下走一步,f(i, j) = f(i - 1, j) + grid(i, j)
向右走一步,f(i, j) = f(i, j - 1) + grid(i, j)

时间复杂度:O(mn)
其中 m 和 n 分别是网格的行数和列数。
空间复杂度:O(mn)
*/
class Solution_0 {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
if (m == 0) return 0;

vector<vector<int>> f(m, vector<int>(n, INT_MAX));
for (int i = 0; i < m; i ++) {
for (int j = 0; j < n; j ++) {
if (i == 0 && j == 0) f[i][j] = grid[i][j];
else {
if (i) f[i][j] = min(f[i][j], f[i - 1][j] + grid[i][j]);
if (j) f[i][j] = min(f[i][j], f[i][j - 1] + grid[i][j]);
}
}
}

return f[m - 1][n - 1];
}
};

/*
思路同上

时间复杂度:O(mn)
空间复杂度:O(mn)
*/
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
if (m == 0) return 0;

vector<vector<int>> f(m, vector<int>(n, INT_MAX));
for (int i = 0; i < m; i ++) {
for (int j = 0; j < n; j ++) {
if (i == 0 && j == 0) f[i][j] = grid[i][j];
else if (i == 0) f[i][j] = f[i][j - 1] + grid[i][j];
else if (j == 0) f[i][j] = f[i - 1][j] + grid[i][j];
else f[i][j] = min(f[i - 1][j], f[i][j - 1]) + grid[i][j];
}
}

return f[m - 1][n - 1];
}
};

/*
思路同上,优化空间

时间复杂度:O(mn)
空间复杂度:O(n)
*/
class Solution_2 {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
if (m == 0) return 0;

vector<int> f(n, INT_MAX);
f[0] = grid[0][0];
// 初始化第一行
for (int j = 1; j < n; j ++) {
f[j] = f[j - 1] + grid[0][j];
}
// 处理剩余的行
for (int i = 1; i < m; i ++) {
f[0] += grid[i][0];
for (int j = 1; j < n; j ++) {
f[j] = min(f[j], f[j - 1]) + grid[i][j];
}
}

return f[n - 1];
}
};

int main() {
Solution solution;

// 测试用例1
vector<vector<int>> obstacleGrid1 = {
{1, 3, 1},
{1, 5, 1},
{4, 2, 1}
};
cout << "Input: " << endl;
printArray(obstacleGrid1);
cout << "Output: " << solution.minPathSum(obstacleGrid1) << endl;

// 测试用例2
vector<vector<int>> obstacleGrid2 = {
{1, 2, 3},
{4, 5, 6}
};
cout << "Input: " << endl;
printArray(obstacleGrid2);
cout << "Output: " << solution.minPathSum(obstacleGrid2) << endl;

return 0;
}

Golang 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package main

import (
"fmt"
)

/*
动态规划:
使用二维数组 dp,其中 dp[i][j] 表示从左上角到 (i, j) 的最小路径和。
初始化起点 dp[0][0] = grid[0][0]。
初始化第一行和第一列,因为这些位置只能从左边或上边到达。
动态规划状态转移方程:dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])。
边界条件:
如果网格为空,返回 0。

时间复杂度:O(mn)
遍历整个 m * n 的网格,时间复杂度为 O(mn)。
空间复杂度:O(mn)
使用了一个大小为 m * n 的二维数组 dp,空间复杂度为 O(mn)。
*/
// minPathSum 返回从左上角到右下角的最小路径和
func minPathSum(grid [][]int) int {
if len(grid) == 0 || len(grid[0]) == 0 {
return 0
}

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 j := 1; j < n; j ++ {
dp[0][j] = dp[0][j-1] + grid[0][j]
}
// 初始化第一列
for i := 1; i < m; i ++ {
dp[i][0] = dp[i-1][0] + grid[i][0]
}

// 动态规划填表
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]
}

/*
思路同上,优化空间

时间复杂度:O(mn)
空间复杂度:O(n)
*/
func minPathSum_1(grid [][]int) int {
if len(grid) == 0 || len(grid[0]) == 0 {
return 0
}

m, n := len(grid), len(grid[0])
dp := make([]int, n) // 使用一维数组存储当前行的状态

// 初始化第一行
dp[0] = grid[0][0]
for j := 1; j < n; j ++ {
dp[j] = dp[j-1] + grid[0][j]
}

// 动态规划填表
for i := 1; i < m; i ++ {
dp[0] += grid[i][0] // 更新第一列
for j := 1; j < n; j ++ {
dp[j] = grid[i][j] + min(dp[j], dp[j-1])
}
}

return dp[n-1]
}

func main() {
// 测试用例
testCases := []struct {
grid [][]int
expected int
}{
{
grid: [][]int{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}},
expected: 7,
},
{
grid: [][]int{{1, 2, 3}, {4, 5, 6}},
expected: 12,
},
}

for i, tc := range testCases {
result := minPathSum(tc.grid)
fmt.Printf("Test Case %d, Input: grid = %v\n", i+1, tc.grid)

if result == tc.expected {
fmt.Printf("Test Case %d, Output: %d, PASS\n", i+1, result)
} else {
fmt.Printf("Test Case %d, Output: %d, FAIL (Expected: %d)\n", i+1, result, tc.expected)
}
}
}

leetcode64:最小路径和
https://lcf163.github.io/2023/11/16/leetcode64:最小路径和/
作者
乘风的小站
发布于
2023年11月16日
许可协议