【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现

这篇具有很好参考价值的文章主要介绍了【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++

君兮_的个人主页

即使走的再远,也勿忘启程时的初心

C/C++ 游戏开发

Hello,米娜桑们,这里是君兮_,博主最近一直在钻研动态规划算法,最近在Leetcode上刷题的时候遇到一个Hard难度的动态规划题,今天就借此机会来给大家分享一下我对这个题目的一些看法和解题思路(放心,我是AC了的)

  • 好了废话不多说,开始我们今天的学习吧!!

地下城游戏

  • Leetcode上的原题链接在这里:地下城游戏

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++

  • 好好好,一看题目里一大堆字还看不懂它到底什么意思,再看看上面标的hard难度,一大堆人相信和博主一样上来就准备先点击退出了,大家先不要捉急,我来带大家一步一步分析一下这个题目的意思

题目解析

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++
(ps:这个在漫画里真是公主)

  • 我们的公主被抓住关在了最右下角,如图所示
    【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++
  • 接下来,我们的骑士要从图中位置出发,其中遇到恶魔(也就是格子里的值为负值)就需要与它们战斗会扣血,当遇到魔法球(图中为正值),就可以回血。此时,题目问我们,在初始位置时,骑士至少需要多少血(规定当在某个位置血量大于等于1即可通过否则失败)
  • 那么,通过题目的描述,结合之前我们学过的动态规划思想,你发现什么不一样了吗?作为Hard难度的题,想用常规的思维来解决肯定是不可能的,好了,接下来我带大家具体分析一下其中的算法原理吧

算法原理

1. 状态表示

  • 我们之前在动态规划的算法中说过,遇到动态规划问题时,一般的解决方式就是分两种情况:
    • (1) 选择某一个位置为终点结束,建立dp表,进行状态表示
    • 2)选择某一个位置为起点出发…
  • 按照常规思路,我们既然知道了公主的位置,那正常情况就是选择第一种情况来试着进行状态表示
  • 这道题如果我们照着这个思路定义成:从起点开始,到达[i, j] 位置的时候,所需的最低初始健康点数。
  • 那么我们分析状态转移的时候会有⼀个问题:那就是我们当前的健康点数还会受到后面的路径的影响。也就是从上往下的状态转移不能很好地解决问题。

这里是为什么呢?我们设想一下,假设此时我们骑士的血很少,下一格无论是朝下还是朝右都会遇到恶魔把我们骑士的血扣为负数,那此时这里的dp值合理吗?很显然是不合理的。因此我们出了考虑前面位置的情况,还要考虑后面路径的情况,岂不是太麻烦了?

  • 这个时候我们要换⼀种状态表示:从[i, j] 位置出发,到达终点时所需要的最低初始健康点数。这样我们在分析状态转移的时候,前面的路径不需要考虑,后续的最佳状态已经知晓,这样就极大的简化了我们分析的难度。

  • 综上所述,定义状态表示为:
    dp[i][j] 表示:从[i, j] 位置出发,到达终点时所需的最低初始健康点数


2 状态转移方程

  • 对于 dp[i][j] ,从 [i, j] 位置出发,下⼀步会有两种选择(为了方便理解,设 dp[i][j] 的最终答案是 x):

  • i. ⾛到右边,然后⾛向终点

  • 那么我们在 [i, j] 位置的最低健康点数加上这⼀个位置的消耗,应该要⼤于等于右边位置的最低健康点数,也就是: x + dungeon[i][j] >= dp[i][j + 1] 。
    通过移项可得: x >= dp[i][j + 1] - dungeon[i][j] 。因为我们要的是最⼩值,因此这种情况下的 x = dp[i][j + 1] - dungeon[i][j]

  • ii. ⾛到下边,然后⾛向终点

  • 那么我们在 [i, j] 位置的最低健康点数加上这⼀个位置的消耗,应该要⼤于等于下边位置的最低健康点数,也就是: x + dungeon[i][j] >= dp[i + 1][j] 。
    通过移项可得: x >= dp[i + 1][j] - dungeon[i][j] 。因为我们要的是最⼩值,因此这种情况下的 x = dp[i + 1][j] - dungeon[i][j]

  • 综上所述,我们需要的是两种情况下的最⼩值,因此可得状态转移⽅程为:
    dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]

  • 但是,如果当前位置的 dungeon[i][j] 是⼀个⽐较⼤的正数的话, dp[i][j] 的值可能变成 0 或者负数。也就是最低点数会⼩于 1 ,那么骑⼠就会死亡。因此我们求出来的 dp[i][j] 如果⼩于等于 0 的话,说明此时的最低初始值应该为 1 。处理这种情况仅需让 dp[i][j] 与 1 取⼀个最⼤值即可:
    dp[i][j] = max(1, dp[i][j])

什么意思呢?就是这里的[i,j]会给恢复一大口血,但是如果此时的dp[i,j]为负数的时候,说明此时这里要求的骑士的最低血量是0或者负数,这显然是不符合要求的,因此我们需要对这种特殊情况进行一下上述的这种处理

初始化

  • 可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:
  • i. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
  • ii. 「下标的映射关系」。

有关辅助节点的使用方法在上面链接的博客中讲过了,这里就不再详叙

  • 在本题中,由于我们要考虑后面路径对现在位置的影响,需要在dp表最后面添加一行,并且添加⼀列后,所有的值都先初始化为无穷大,然后让dp[m][n - 1] 或dp[m - 1][n] = 1 即可。

填表顺序

  • 根据「状态转移方程」,我们需要「从下往上填每一行」,「每一行从右往左填」。看了上面的算法分析这一点应该不难理解

返回值

  • 从题目中可知,我们的骑士是从左上角开始的,因此结合上述分析,我们需要返回的值为dp[0][0]

编写代码

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m=dungeon.size();
        int n=dungeon[0].size();
        //建立dp表,以某个位置为开始建立状态转移方程
        vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
        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+1][j],dp[i][j+1])-dungeon[i][j];
                dp[i][j]=max(1,dp[i][j]);
            }
        }
        //返回值
        return dp[0][0];

    }
};
  • 代码很简单,只有十几行,实际上难的是上面分析题目的过程以及对一些特殊情况的判断,代码这里相信如果你能看懂上述原理的分析,这点对你来说应该一点都不难。

总结

  • 好啦,我们今天的内容就先到这里啦!其实代码并不重要,能看懂背后隐藏的原理并且通过这个题目学会对应题目的分析才重要,因此如果你想真正学会的话,不妨自己从头试着理解一下算法原理再自己独立编写代码,这样我相信是最能提升自己有关动态规划题目的理解的。
  • 有任何的问题和对文章内容的疑惑欢迎在评论区中提出,当然也可以私信我,我会在第一时间回复的!!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现,每日易题,leetcode,动态规划,游戏,算法,c++文章来源地址https://www.toymoban.com/news/detail-751637.html

到了这里,关于【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【LeetCode】动态规划类题目详解

    所有题目均来自于LeetCode,刷题代码使用的Python3版本 如果某一个问题有重叠的子问题,则使用动态规划进行求解是最有效的。 动态规划中每一个状态一定是由上一个状态推导出来的,这一点区别于贪心算法 动态规划五部曲 确定dp数组以及下标的含义 确定递推公式 dp数组如何

    2024年04月11日
    浏览(48)
  • 【leetcode 力扣刷题】回文串相关题目(KMP、动态规划)

    题目链接:5. 最长回文子串 题目内容: 题目就是要我们找s中的回文子串,还要是最长的。其实想想,暴力求解也行……就是遍历所有的子串,同时判断是不是回文串,是的话再和记录的最大长度maxlen比较,如果更长就更新。时间复杂度直接变成O(n^3)。 优化的点在于,假设子

    2024年02月09日
    浏览(48)
  • ( 动态规划) 1035. 不相交的线 ——【Leetcode每日一题】

    难度:中等 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足: nums1[i] == nums2[j] 且绘制的直线不与任何其他连线(非水平线)相交。 请注意,连线即使在端点也不能相交

    2024年02月05日
    浏览(43)
  • ( 动态规划) 516. 最长回文子序列 ——【Leetcode每日一题】

    难度:中等 给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 示例 1: 输入:s = “bbbab” 输出:4 解释:一个可能的最长回文子序列为 “bbbb” 。 示例

    2024年02月06日
    浏览(43)
  • (动态规划) 132. 分割回文串 II ——【Leetcode每日一题】

    难度:困难 给你一个字符串 s ,请你将 s 分割成一些子串,使每个子串都是回文。 返回符合要求的 最少分割次数 。 示例 1: 输入:s = “aab” 输出:1 解释:只需一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子串。 示例 2: 输入:s = “a” 输出:0 示例 3: 输入:

    2024年02月15日
    浏览(46)
  • 动态规划——地下城游戏

    leetcode在线oj题——地下城游戏 恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士的初始健康点数为一个正整数。如果他的

    2024年02月11日
    浏览(48)
  • 【学会动态规划】地下城游戏(10)

    目录 动态规划怎么学? 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后: 学习一个算法没有捷径,更何况是学习动态规划, 跟我一起刷动态规划算法题,一起学会动态规划! 题目链接:174. 地下城游戏 - 力扣(Lee

    2024年02月14日
    浏览(62)
  • (动态规划) 剑指 Offer 60. n个骰子的点数 ——【Leetcode每日一题】

    难度:中等 把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s 。输入 n ,打印出s的所有可能的值出现的概率。 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。 示例 1: 输入: 1 输出: [0.16667,0.16667,0.16667,

    2024年02月11日
    浏览(45)
  • (动态规划) 剑指 Offer 42. 连续子数组的最大和 ——【Leetcode每日一题】

    难度:简单 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 要求时间复杂度为 O(n) 。 示例1: 输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 提示 : 1 = a r r . l e n g t h = 1 0 5 1 = arr.length = 10^

    2024年02月11日
    浏览(52)
  • ( 动态规划) 674. 最长连续递增序列 / 718. 最长重复子数组——【Leetcode每日一题】

    难度:简单 给定一个未经排序的整数数组,找到最长且 连续递增的子序列 ,并返回该序列的长度。 连续递增的子序列 可以由两个下标 l 和 r(l r) 确定,如果对于每个 l = i r ,都有 nums[i] nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

    2024年02月05日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包