《LeetCode 64 最小路径和 || LeetCode 174 地下城游戏》

一、题目

二、做题思路

2.1 状态表示(核心基础)

本题要求计算从左上角到右下角的路径上数字总和的最小值 。由于网格是二维的,我们定义 dp[i][j] 表示从起点 (0,0) 到达网格第 i-1 行第 j-1 列位置时的 最小累计路径和**。**

2.2 状态转移方程(关键难点)

每次只能向右或向下 移动一步,因此到达当前格子的最后一步 要么来自上方 (i-1, j),要么来自左方 (i, j-1)。为了获得最小和 ,我们选择两者中累计路径和较小的那个,再加上当前格子的数值 grid[i-1][j-1]

dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1]

2.3 初始化(边界防护)

由于多开了一行一列,且我们希望递推公式对所有格子(包括起点)统一生效,需要特殊处理虚拟边界:

  • dp 数组所有元素初始化为 INT_MAX(表示不可达)。

  • 设置 dp[0][1] = dp[1][0] = 0 ,作为虚拟起点的"入口",使得计算 起点 (1,1) 时,min(dp[0][1], dp[1][0]) = 0,从而正确得到 dp[1][1] = grid[0][0]

2.4 填表顺序(递推方向)

计算 dp[i][j] 依赖 dp[i-1][j](上方)和 dp[i][j-1](左方),这些位置的行号或列号均小于当前值 。因此必须从上到下、从左到右 (即先行后列,双重循环递增)依次填充 dp 表,确保每个状态计算时,其所有前置状态均已就绪

2.5 返回值(目标映射)

题目要求返回从起点到达右下角的最小路径和 。右下角在原网格中的坐标为 (m-1, n-1),对应 dp 表中的 dp[m][n] (因为多开了一行一列)。因此直接返回 dp[m][n],该值即为所求。

三、代码

cpp 复制代码
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        // 1. 创建dp表
        int row = grid.size();
        int col = grid[0].size();
        // 多开一行一列(dp[row+1][col+1])作为虚拟边界,便于统一处理边界情况
        // 所有位置初始化为 INT_MAX,使得只有有效路径才会被更新
        vector<vector<int>> dp(row + 1, vector<int>(col + 1, INT_MAX));

        // 2. 初始化
        // 技巧:将虚拟起点设在 (0,1) 和 (1,0),使它们为 0,
        // 这样在计算 dp[1][1](真正的起点)时,min(dp[0][1], dp[1][0]) = 0,
        // 加上 grid[0][0] 后得到正确的起点路径和。
        // 这种写法避免了单独处理起点的繁琐,让递推公式对所有格子统一生效。
        dp[0][1] = dp[1][0] = 0;

        // 3. 填表顺序:从上到下,从左到右
        // 因为 dp[i][j] 依赖于 dp[i-1][j](上方)和 dp[i][j-1](左方),
        // 按行遍历即可保证依赖的子问题已经计算完毕。
        for (int i = 1; i <= row; i++) {
            for (int j = 1; j <= col; j++) {
                // 4. 状态转移方程:
                // 到达当前格子的最小路径和 = 从上方或左方来的较小路径和 + 当前格子的权值
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
            }
        }

        // 5. 返回值:dp[row][col] 即为从起点 (0,0) 到终点 (row-1, col-1) 的最小路径和
        return dp[row][col];
    }
};

四、流程图

五、题目

六、做题思路

2.1 状态表示(核心基础)

本题要求计算从起点出发到达终点所需的最低初始健康点数 。由于路径决策依赖未来(后续房间的消耗/增益),我们采用从终点向起点逆推 的方式。定义 dp[i][j] 表示从位置 (i, j) 出发,到达右下角终点所需的最小初始生命值 (即进入 (i, j) 前至少需要多少血量)。

2.2 状态转移方程(关键难点)

骑士每次只能向右或向下 移动一步,因此从 (i, j) 出发的下一步 要么去右方 (i, j+1),要么去下方 (i+1, j)。为了确保后续路径存活,进入 (i, j) 前的血量必须满足:扣除当前房间的数值后,仍能支撑下一步所需的最小血量 。因此,先计算 min(dp[i][j+1], dp[i+1][j])(选择右、下中所需血量较小的方向),再减去当前房间的消耗/增益 dungeon[i][j],得到进入该房间前所需的理论最低血量,最后保证至少为 1 (血量不能为 0 或负):

dp[i][j] = max(1, min(dp[i][j+1], dp[i+1][j]) - dungeon[i][j])

2.3 初始化(边界防护)

为避免在右下角边界外访问,采用多开一行一列dp 尺寸为 (m+1)×(n+1)),所有元素初始化为 INT_MAX (表示不可达)。关键技巧:将终点的右方下方 虚拟格子设为 1 ,即 dp[m][n-1] = dp[m-1][n] = 1。这表示从终点 (m-1, n-1) 到达终点(即停留在终点)所需的最小生命值为 1,使得计算终点时 min(1, 1) - dungeon[m-1][n-1] 能正确推导出进入终点前所需血量。

2.4 填表顺序(递推方向)

计算 dp[i][j] 依赖 dp[i][j+1](右方)和 dp[i+1][j](下方),这些位置的行号或列号均大于当前值 。因此必须从右下角向左上角 (即 im-10jn-10)依次填充 dp 表,确保每个状态计算时,其所有后续状态均已就绪

2.5 返回值(目标映射)

题目要求返回从起点 (0,0) 出发拯救公主所需的最低初始健康点数dp[0][0] 正好表示该值。因

七、代码

cpp 复制代码
class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int row = dungeon.size();
        int col = dungeon[0].size();

        // 1. 创建dp表
        // dp[i][j] 表示从位置 (i, j) 到达右下角 (row-1, col-1) 所需的最小初始生命值
        // 多开一行一列(dp[row+1][col+1])作为虚拟边界,便于处理右下角边界
        // 所有位置初始化为 INT_MAX,只有有效路径才会被更新
        vector<vector<int>> dp(row + 1, vector<int>(col + 1, INT_MAX));

        // 2. 初始化
        // 技巧:将终点右方和下方的虚拟格子设为 1,
        // 表示从终点 (row-1, col-1) 走到终点(即停留在终点)所需的最小生命值为 1。
        // 这样在计算 dp[row-1][col-1] 时,min(dp[row-1][col], dp[row][col-1]) = 1,
        // 从而正确得到从终点出发至少需要 1 点血。
        dp[row][col - 1] = dp[row - 1][col] = 1;

        // 3. 填表顺序:从右下角向左上角遍历(逆序)
        // 因为 dp[i][j] 依赖于 dp[i][j+1](右方)和 dp[i+1][j](下方),
        // 所以从最后一行/列开始,逆序遍历即可保证依赖的子问题已计算。
        for (int i = row - 1; i >= 0; i--) {
            for (int j = col - 1; j >= 0; j--) {
                // 4. 状态转移方程:
                // 进入当前格子前所需的最小血量 = 
                //   max(1, min(从右方进入所需血量, 从下方进入所需血量) - 当前格子的消耗/收益)
                // 其中 min(...) - dungeon[i][j] 表示进入当前格子并扣除/增加血量后,仍能满足后续需求
                // 再与 1 取 max 保证进入时血量至少为 1(生命值不能为 0 或负数)
                int need = min(dp[i][j + 1], dp[i + 1][j]) - dungeon[i][j];
                dp[i][j] = max(1, need);
            }
        }

        // 5. 返回值:dp[0][0] 即为从起点 (0,0) 到达右下角所需的最小初始生命值
        return dp[0][0];
    }
};

八、流程图

相关推荐
森林古猿11 小时前
再论斜率优化
c++·学习·算法
万法若空1 小时前
【数学-简单数论】同余中的逆
线性代数·算法
水利行业RTU手艺人1 小时前
RTU固件中的“数据保险箱”——离线数据补发系统设计
c++·stm32·单片机·物联网
凯瑟琳.奥古斯特2 小时前
力扣1009补码解法C++实现
开发语言·c++·算法·leetcode·职场和发展
KaMeidebaby2 小时前
卡梅德生物技术快报|蛋白的叠氮基修饰:实操解析:核酸模板耦合蛋白的叠氮基修饰实现靶蛋白定点共价标记
前端·人工智能·物联网·算法·百度
通信仿真爱好者3 小时前
第【60期】--大规模MIMO系统信号检测算法误码率比较 --matlab完整代码+参考文章
算法·matlab·mimo·信号检测
AI科技星4 小时前
全域谱分析:无穷维超复数信息场分形统一场论——自然、量子、金融多重分形第一性原理完整体系(中英双语终稿)
人工智能·python·算法·金融·乖乖数学·全域数学
先吃饱再说5 小时前
为什么堆能 O(log n) 插入?拆解完全二叉树的数组魔法
算法·排序算法
zh_xuan5 小时前
c++ optional用法
开发语言·c++·optional