LeetCode 64. Minimum Path Sum 动态规划详解

本文整理这道题的题意、思路推导、状态设计、边界处理,以及一份 C 语言实现,并顺带对一些容易在面试中被问到的细节做说明。leetcode+1​

题目理解

给定一个 m x n 的网格 grid,每个格子里是一个非负整数。leetcode

从左上角 (0,0) 出发,只能向右或向下移动,走到右下角 (m-1, n-1)。leetcode

要求:找到一条路径,使路径上所有格子数字之和最小,并返回这个最小和。leetcode

示例:

grid = \[1,3,1,1,5,1,4,2,1],答案为 7,路径为 1 → 3 → 1 → 1 → 1。leetcode

grid = \[1,2,3,4,5,6],答案为 12。leetcode

约束:

1 <= m, n <= 200,0 <= gridij <= 200。leetcode

动态规划思路推导

1. 状态定义

设一个与 grid 同大小的二维数组 dp:

状态含义:dpij 表示从起点 (0,0) 走到格子 (i,j) 的最小路径和。

这样,最终目标就是求 dpm-1n-1

2. 状态转移

从起点往终点推导每个格子的最小路径和:

起点:

dp00 = grid00

第一行 i == 0:

只能从左边走过来:

dp0j = dp0j-1 + grid0j

第一列 j == 0:

只能从上边走过来:

dpi0 = dpi-10 + gridi0

其他位置 (i > 0, j > 0):

只能从上方 (i-1,j) 或左方 (i,j-1) 过来,取路径和较小的那条:

dpij = min(dpi-1j, dpij-1) + gridij

这正好对应你一开始写下的转移方程,思路是正确的,只是最后返回值当时写成了"最后一行的最小值",与题目"必须到右下角"不一致,后面你在代码中已经修正为右下角坐标。leetcode

边界与实现细节

1. 初始化与默认值

在 C 代码中,为了方便统一转移,你给 dp 初始填了一个很大的值 INF_MAX = 0x7fffffff:

c 复制代码
#define INF_MAX 0x7fffffff

然后 dpij 全部初始化为 INF_MAX,再从 (0,0) 开始,用转移公式迭代更新。

实际上这道题因为边界(i == 0 和 j == 0)单独处理了,INF_MAX 更多是防御性初始化,可以保留,也可以不依赖它来写逻辑。leetcode

2. 边界跳过起点

代码里:

c 复制代码
if (i == 0 && j == 0)
    continue;

对 (0,0) 直接跳过,是因为已经单独设定了 dp00 = grid00,避免在转移时又被覆盖或访问到负索引。

这在面试时是加分点:说明清楚起点是"手动初始化",循环里只处理除起点外的其他格子。leetcode

C 语言代码实现

下面是你提交并 AC 的 C 代码,时间 0ms,击败 100%,逻辑清晰、边界正确、空间也正常释放了内存。leetcode

c 复制代码
#include <stdlib.h>

#define INF_MAX 0x7fffffff
#define MIN(a, b) ((a) < (b) ? (a) : (b))

int minPathSum(int **grid, int gridSize, int *gridColSize) {
    int i, j, result, col_size;
    int **dp;

    dp = (int **)malloc(gridSize * sizeof(int *));
    for (i = 0; i < gridSize; i++) {
        col_size = gridColSize[i];
        dp[i] = (int *)malloc(col_size * sizeof(int));
        for (j = 0; j < col_size; j++)
            dp[i][j] = INF_MAX;
    }

    dp[0][0] = grid[0][0];

    for (i = 0; i < gridSize; i++) {
        col_size = gridColSize[i];
        for (j = 0; j < col_size; j++) {
            if (i == 0 && j == 0)
                continue;
            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] = MIN(dp[i][j - 1] + grid[i][j],
                               dp[i - 1][j] + grid[i][j]);
        }
    }

    col_size = gridColSize[gridSize - 1];
    result = dp[gridSize - 1][col_size - 1];

    for (i = 0; i < gridSize; i++)
        free(dp[i]);
    free(dp);

    return result;
}

复杂度分析:

时间复杂度:遍历整张表一次,O(mn)。

空间复杂度:使用了一个与 grid 同大小的 dp 数组,O(mn) 额外空间。leetcode

可能的面试追问与优化方向

能否不使用额外二维 dp 数组,直接在 grid 上原地累加,从而把额外空间从 O(mn) 优化到 O(1)?leetcode

如果只用一维数组(按行或按列滚动),如何写状态转移,空间复杂度可以优化到 O(min(m,n))?leetcode

https://leetcode.com/problems/minimum-path-sum/?envType=study-plan-v2&envId=top-interview-150
https://leetcode.com/problems/minimum-path-sum/description/?envType=study-plan-v2&envId=top-interview-150
https://leetcode.com/problems/minimum-path-sum/submissions/1871120418/?envType=study-plan-v2&envId=top-interview-150

相关推荐
白狐_79813 分钟前
408数据结构第5章:树与二叉树②——遍历、线索树、森林与哈夫曼
数据结构·算法·深度优先
致Great3 小时前
科研人的 AI,不该只回答问题:我用字节TraeWork 跑了一遍真实研究任务
算法
纵有疾風起3 小时前
线性表的定义与基本操作 — 从逻辑结构到 ADT 接口
数据结构·算法·408·线性表·adt
测试_AI_一辰4 小时前
AI Agent 评测最隐蔽的坑-记忆
人工智能·算法·ai·自动化·ai编程
lucas_AI4 小时前
给YOLO检测器插 LoRA,'插对地方'比'插什么'更要命
人工智能·算法
LuminousCPP5 小时前
数据结构-时间与复杂度|时间/空间复杂度 + 两道力扣练习 + 二分查找复盘
c语言·数据结构·经验分享·算法·leetcode
Nil2085 小时前
leetcode 三数之和
数据结构·算法·leetcode
小小龙学IT5 小时前
C++ std::vector 底层实现深度解析:内存布局、扩容策略、移动语义与迭代器失效
开发语言·c++·算法
noipp5 小时前
推荐题目:洛谷 P16689 出征
java·开发语言·数据结构·c++·算法·洛谷·luogu
eBest数字化转型方案5 小时前
从工程视角拆解冰柜资产管理:拍照识别 pipeline、纯净度算法与 IoT 选型踩坑
人工智能·物联网·算法