[动态规划] (十) 路径问题 LeetCode 174.地下城游戏

动态规划 (十) 路径问题: LeetCode 174.地下城游戏

文章目录

174. 地下城游戏

题目解析

先明白下题题再来看。

[动态规划 (四) LeetCode 91.解码方法-CSDN博客](https://blog.csdn.net/dev1cce/article/details/134210032?spm=1001.2014.3001.5501)

(1) 骑士拯救公主:从左上角到右下角

(2) 每个格子都是一个房间,房间内有恶魔或者魔法球

(3) 恶魔扣除血量,魔法球增加血量

(4) 骑士只能向右或者向下移动

(5) 血量小于等于0,骑士死亡

(6) 返回拯救公主,最少需要的血量

解题思路
状态表示

按照以往的经验,我们都是设定以(i,j)位置为终点所需要的最低血量,但是这道题不太方便。

示例1:dungeon = \[-2,-3,3,-5,-10,1,10,30,-5]

假如一开始,你设定最小血量为3。

  • 第一次,3 - 2 => 1-3 => -2,又得回去修改初始血量为6,
  • 第二次,6-2-3=> 1+3+1=>5-5 = 0,又回去设定初始血量为7,
  • 第三次,7-2-3+3+1 => 6-5=>1 ,终于救出了公主。

这种情况有个专业的名词,后置性。(想要了解大家可以去自行查资料)

总之,这种方法很难实现,所以我们选择第二种方法。

以(i,j)为起点到达终点所需要的最低血量,即dpi j

状态转移方程

从(i,j)位置到下一步,也就是到达(i,j+1)或者(i+1,j)位置。

所以到达(i,j)位置后加上当前位置的值dungeon(i,,j),必须大于等于下一位置的值。

即骑士到达(i,j)位置,击败(i,j)位置上的恶魔后,血量必须大于击败下一个恶魔所消耗的血量。也就是如上图,骑士击败-2位置的恶魔后的血量,必须大于-3,也就是等于6。

shell 复制代码
dp[i][j] + dungeon[i][j] >= min(dp[i][j+1], dp[i+1][j])

当然,房间内也有可能是魔法球(加血),到达dpi j前有可能血量已经小于等于0了,我们必须让它的最小值是1即可。

即骑士到达(i,j)位置,获得(i,j)位置上的魔法球前血量必须最小为1。

如上图,骑士接连击败-2,-3恶魔后还得有1滴血来获得魔法球。

为了不让dpi j为负数,

shell 复制代码
dp[i][j] = max(1, dp[i][j])
初始化和填表顺序
  • 初始化

我们访问的是j+1、i+1的位置,所以一般从右下角开始初始化。

和以往不同,我们初始化时,与1-6号位置有边界情况,如图。

1号位置与右边和下边的位置有影响,让它初始化为1,即可保证击败公主位置上的恶魔后至少还有1滴血。

2、3、4、5、6等位置的下边或者旁边,因为多了一行或者一列,为了不让骑士走出恶魔的城堡,初始化为整数的最大值即可。

  • 填表顺序

倒着一列一列填表即可,参考状态表示,一步一步推出上一位置所需要的最少血量,直到(0,0)位置。

返回值

返回(0,0)位置即可,与我们得出的状态表示相同。

看到这里,大家可以尝试实现一下代码,然后再看后面的内容。


代码实现
shell 复制代码
class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        //创建一个dp数组
        int m = dungeon.size(), n = dungeon[0].size();
        vector<vector<int>> dp(m+1, vector<int>(n+1, INT_MAX));
        //初始化
        dp[m-1][n] = dp[m][n-1] = 1;
        //填表
        for(int i = m-1; i >= 0; i--)
            for(int j = n-1; j >= 0; j--)
            {
                dp[i][j] = min(dp[i][j+1], dp[i+1][j]) - dungeon[i][j];
                dp[i][j] = max(1, dp[i][j]);
            }
        //返回值
        return dp[0][0];
    }
};
总结

细节1:我们的下标对应的原数组并没有因为我们扩大一列和一行而改变,因为我们扩大的是最后一列最后一行。

细节2:走到下一位置时至少还要保证有1滴血,若是血量 <= 0,让它至少要为1滴血,否则无法进入下一个房间。

相关推荐
深海潜水员3 小时前
【从零开始的C#游戏开发课程】- FarmStory1.0 日志系统和游戏资源的管理
游戏·c#·monogame
兰令水3 小时前
leecodecode【区间DP+树形DP】【2026.6.10打卡-java版本】
java·算法·leetcode
8Qi84 小时前
LeetCode 4:寻找两个正序数组的中位数 —— 二分查找法
java·算法·leetcode·职场和发展·二分查找
8Qi84 小时前
LeetCode 32:最长有效括号 —— 栈 + 标记法 题解
java·数据结构·算法·leetcode·职场和发展··括号匹配
Tairitsu_H4 小时前
[LC优选算法#3] 滑动窗口 | 将x减到0的最⼩操作数 | ⽔果成篮 | 字⺟异位词
c++·算法·leetcode·滑动窗口
我还记得那天4 小时前
C语言随机数生成机制与猜数字游戏实现
c语言·开发语言·游戏
Swift社区4 小时前
鸿蒙游戏如何实现多端一致性?
游戏·华为·harmonyos
德迅云安全-上官4 小时前
游戏盾的原理解析与游戏盾的优势特点
游戏
洛水水4 小时前
【力扣100题】76.搜索插入位置
数据结构·算法·leetcode
wabs6665 小时前
关于动态规划【力扣343.整数拆分的递推公式怎么理解?】
算法·leetcode·动态规划