「动态规划」如何求地下城游戏中,最低初始健康点数是多少?

2024-06-07 21:44

本文主要是介绍「动态规划」如何求地下城游戏中,最低初始健康点数是多少?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

174. 地下城游戏icon-default.png?t=N7T8https://leetcode.cn/problems/dungeon-game/description/

恶魔们抓住了公主并将她关在了地下城dungeon的右下角。地下城是由m x n个房间组成的二维网格。我们英勇的骑士最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至0或以下,他会立即死亡。有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。为了尽快解救公主,骑士决定每次只向右或向下移动一步。返回确保骑士能够拯救到公主所需的最低初始健康点数。注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

  1. 输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]],输出:7,解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为7。
  2. 输入:dungeon = [[0]],输出:1。

提示:m == dungeon.length,n == dungeon[i].length;1 <= m, n <= 200;-1000 <= dungeon[i][j] <= 1000。


我们用动态规划的思想来解决这个问题。

确定状态表示:根据经验和题目要求,我们有2个状态表示的方案:

  • 用dp[i][j]表示:从起点开始,到达[i, j]位置,所需的最低初始健康点数。
  • 用dp[i][j]表示:从[i, j]位置开始,到达终点,所需的最低初始健康点数。

究竟选择哪一种状态表示呢?事实上,哪一种状态表示能推导出状态转移方程,我们就选择哪一种状态表示。

推导状态转移方程:首先考虑前一种状态表示。考虑最近的一步,要想到达[i, j]位置,只有2种情况:

  • 先到达[i - 1, j]位置,再向下走一步,到达[i, j]位置。
  • 先到达[i, j - 1]位置,再向右走一步,到达[i, j]位置。

如果能推出状态转移方程,那么状态转移方程一定形如dp[i][j] = f(dp[i - 1, j], dp[i, j - 1])。然而,[i, j]右下方的位置是有可能影响到dp[i][j]的。比如,如果右下方有一个房间是-1000,那么所需的初始健康点数就是一个很大的值;如果右下方都是正数,那么可能不需要很大的初始健康点数。也就是说,dp[i][j]和右下方的值相关,但是dp[i][j] = f(dp[i - 1, j], dp[i, j - 1])这个方程与右下方的值无关。从而,我们推导不出状态转移方程。

所以,我们选择后一种状态表示:用dp[i][j]表示:从[i, j]位置开始,到达终点,所需的最低初始健康点数。考虑最近的一步,要想从dp[i][j]位置出发到达终点,只有2种情况:

  • 先向下走一步,到达[i + 1, j]位置,再从[i + 1, j]位置出发到达终点。所以,从[i, j]位置出发到达终点需要的最低初始健康点数dp[i][j],在经历了[i, j]房间后,健康点数变为dp[i][j] + dungeon[i][j],而dp[i][j] + dungeon[i][j]必须至少是从[i + 1, j]位置出发到达终点所需要的最低初始健康点数dp[i + 1][j],即dp[i][j] + dungeon[i][j] >= dp[i + 1][j],从而dp[i][j] >= dp[i + 1][j] - dungeon[i][j],又由于dp[i][j]表示最低初始健康点数,所以dp[i][j] = dp[i + 1][j] - dungeon[i][j]。
  • 先向右走一步,到达[i, j + 1]位置,再从[i, j + 1]位置出发到达终点。同理可得此时dp[i][j] = dp[i][j + 1] - dungeon[i][j]。

从[i, j]位置出发到达终点所需要的最低初始健康点数,应该是上面2种情况的较小值,即dp[i][j] = min(dp[i + 1][j] - dungeon[i][j], dp[i][j + 1] - dungeon[i][j]) = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]。

然而这个状态转移方程有个很大的漏洞。如果min(dp[i + 1][j], dp[i][j + 1]) <= dungeon[i][j],那么dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j] <= 0。然而血量是不能低于0的,所以我们还需要判断一下,如果计算出来的dp[i][j] <= 0,那么dp[i][j] = 1。

综上所述:状态转移方程为:dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j])

初始化:观察状态转移方程,我们在计算dp表最后一行和最后一列的值时,会越界访问。所以,我们要对其初始化。这里我们用增加辅助结点的方式来初始化。我们在dp表的最下面和最右边分别加上一行一列辅助结点。接下来我们考虑,如何初始化辅助结点,才能保证后续的填表是正确的。我们把此时的dp表画出来:

      ? *? *
? ? ? ? *
* * * * *

先考虑右下角的?位置。这个?位置表示,直接从dungeon的右下角出发,到达右下角,所需要的最低初始健康点数。显然这个?位置的值只需要保证,在更新完处于dungeon的右下角的健康点数之后,其值依然大于等于1,也就是说,如果dungeon的右下角是正数,那么?位置的值是1;如果dungeon的右下角是负数,那么?位置的值是1减去dungeon的右下角的值(负负得正)。再观察状态转移方程:dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]),我们发现,如果dp[i + 1][j] = dp[i][j + 1] = 1,那么dp[i][j] = max(1, min(1, 1) - dungeon[i][j]) = max(1, 1 - dungeon[i][j]),1代表dungeon的右下角是正数的情况,1 - dungeon[i][j]代表dungeon的右下角是负数的情况,刚好符合预期。所以,对于右下角的?位置,我们要把它的下面和右边的2个*位置的值初始化为1。

      ? *? *
? ? ? ? 1
* * * 1 *

接着考虑除了右下角的?位置之外,其余的?位置。观察状态转移方程: dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]),我们发现,dp[i + 1][j]和dp[i][j + 1]会涉及到辅助结点。我们只需要把这些辅助结点初始化为+∞,在计算min(dp[i + 1][j], dp[i][j + 1])时,辅助结点的值就不会影响到结果了。由于并没有导致溢出风险的运算,我们用INT_MAX代表+∞即可。

综上所述:我们在dp表的最下面和最右边分别加上一行一列辅助结点,并且把[m - 1, n]和[m, n - 1]位置的值初始化为1,其余辅助结点初始化为INT_MAX

填表顺序:根据状态转移方程,dp[i][j]依赖于dp[i + 1][j]和dp[i][j + 1],所以应从下往上,从右往左填表

返回值:应返回dp表左上角的值,即dp[0][0]

细节问题:由于新增了一行一列辅助结点,dp表的规模比dungeon的规模大一行一列,即dp表的规模为(m + 1) x (n + 1)。由于辅助结点是在dp表的右下方,并不影响下标的映射关系,所以dp表的[i, j]位置依然对应dungeon的[i, j]位置。

时间复杂度:O(m x n),空间复杂度:O(m x n)。

class Solution {
public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m = dungeon.size(), n = dungeon[0].size();// 创建dp表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] =max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]);}}// 返回结果return dp[0][0];}
};

这篇关于「动态规划」如何求地下城游戏中,最低初始健康点数是多少?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040384

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

轨迹规划-B样条

B样条究竟是干啥的?白话就是给出一堆点,用样条的方式,给这些点连接起来,并保证丝滑的。 同时B样条分为准均匀和非均匀,以下为准均匀为例。 参考链接1:https://zhuanlan.zhihu.com/p/50626506https://zhuanlan.zhihu.com/p/50626506 参考链接2: https://zhuanlan.zhihu.com/p/536470972h

火柴游戏java版

代码 /*** 火柴游戏* <p>* <li>有24根火柴</li>* <li>组成 A + B = C 等式</li>* <li>总共有多少种适合方式?</li>* <br>* <h>分析:</h>* <li>除去"+"、"="四根,最多可用火柴根数20根。</li>* <li>全部用两根组合成"1",最大数值为1111。使用枚举法,A和B范围在0~1111,C为A+B。判断</li>** @

国产游戏行业的崛起与挑战:技术创新引领未来

国产游戏行业的崛起与挑战:技术创新引领未来 近年来,国产游戏行业蓬勃发展,技术水平不断提升,许多优秀作品在国际市场上崭露头角。从画面渲染到物理引擎,从AI技术到服务器架构,国产游戏已实现质的飞跃。然而,面对全球游戏市场的激烈竞争,国产游戏技术仍然面临诸多挑战。本文将探讨这些挑战,并展望未来的机遇,深入分析IT技术的创新将如何推动行业发展。 国产游戏技术现状 国产游戏在画面渲染、物理引擎、AI