问题描述
给定一个包含非负整数的二维数组 grid
,找到从左上角到右下角的最小路径和。
示例
输入:
grid =
[ [1, 3, 1],
[1, 5, 1],
[4, 2, 1]]
输出:
7
解释:路径为 1 → 3 → 1 → 1 → 1
,其和为 7。
解题思路
这是一个经典的 动态规划 问题。我们用一个二维数组 dp
,其中 dp[i][j]
表示从左上角到位置 (i, j)
的最小路径和。
状态转移方程
- 起点位置:
dp[0][0] = grid[0][0]
- 第一行:只能从左边过来,所以
dp[0][j] = dp[0][j - 1] + grid[0][j]
- 第一列:只能从上边过来,所以
dp[i][0] = dp[i - 1][0] + grid[i][0]
- 其他位置:可以从上边或左边过来,取路径和较小者:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
优化
为了节省空间,可以直接在原数组 grid
中进行状态更新,避免额外的空间开销。
代码实现
方法 1:动态规划(原地修改)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int minPathSum(int** grid, int gridSize, int* gridColSize) {
int m = gridSize;
int n = *gridColSize;
// 原地更新 grid 作为 dp 数组
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 && j == 0) {
// 起点
continue;
} else if (i == 0) {
// 第一行
grid[i][j] += grid[i][j - 1];
} else if (j == 0) {
// 第一列
grid[i][j] += grid[i - 1][j];
} else {
// 其他位置
grid[i][j] += (grid[i - 1][j] < grid[i][j - 1] ? grid[i - 1][j] : grid[i][j - 1]);
}
}
}
// 返回右下角的最小路径和
return grid[m - 1][n - 1];
}
int main() {
int row1[] = {1, 3, 1};
int row2[] = {1, 5, 1};
int row3[] = {4, 2, 1};
int* grid[] = {row1, row2, row3};
int colSize = 3;
printf("最小路径和: %d\n", minPathSum(grid, 3, &colSize));
return 0;
}
方法 2:动态规划(使用额外的 dp
数组)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int minPathSum(int** grid, int gridSize, int* gridColSize) {
int m = gridSize;
int n = *gridColSize;
// 动态分配 dp 数组
int** dp = (int**)malloc(m * sizeof(int*));
for (int i = 0; i < m; i++) {
dp[i] = (int*)malloc(n * sizeof(int));
}
// 填充 dp 数组
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 && j == 0) {
dp[i][j] = grid[i][j];
} else if (i == 0) {
dp[i][j] = dp[i][j - 1] + grid[i][j];
} else if (j == 0) {
dp[i][j] = dp[i - 1][j] + grid[i][j];
} else {
dp[i][j] = (dp[i - 1][j] < dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1]) + grid[i][j];
}
}
}
// 结果
int result = dp[m - 1][n - 1];
// 释放内存
for (int i = 0; i < m; i++) {
free(dp[i]);
}
free(dp);
return result;
}
int main() {
int row1[] = {1, 3, 1};
int row2[] = {1, 5, 1};
int row3[] = {4, 2, 1};
int* grid[] = {row1, row2, row3};
int colSize = 3;
printf("最小路径和: %d\n", minPathSum(grid, 3, &colSize));
return 0;
}
复杂度分析
时间复杂度
- 遍历整个网格,时间复杂度为 O ( m × n ) O(m \times n) O(m×n)。
空间复杂度
- 方法 1(原地修改):直接使用
grid
,无需额外空间,空间复杂度为 O ( 1 ) O(1) O(1)。 - 方法 2(额外
dp
数组):需要一个同样大小的dp
数组,空间复杂度为 O ( m × n ) O(m \times n) O(m×n)。
总结
- 如果允许修改输入网格,推荐使用 方法 1,空间复杂度低,效率高。
- 如果不允许修改输入数据,可以使用 方法 2,通过额外的数组保存状态。