☆打卡算法☆LeetCode 174. 地下城游戏 算法解析

简介: ☆打卡算法☆LeetCode 174. 地下城游戏 算法解析

大家好,我是小魔龙,Unity3D软件工程师,VR、AR,虚拟仿真方向,不定时更新软件开发技巧,生活感悟,觉得有用记得一键三连哦。

一、题目

1、算法题目

“编写一个函数,来计算骑士能够拯救公主所需的最低初始健康点数。”

2、题目描述

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快到达公主,骑士决定每次只向右或向下移动一步。

编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。

例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。

1702379751069.jpg

说明:

  • 骑士的健康点数没有上限。
  • 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
示例 1:
示例 2:

二、解题

1、思路分析

这种寻找路径的题目,可以想到的就是使用动态规划,动态规划的重点就是找到子结构的规律。

首先,这是一个M * N的网格,每次只能向右或者向下移动一步,然后移动的时候要确保骑士在房间至少要有一点健康点数,直到骑士救出公主,也就是找一个可行的最小值路线。

提取一下有效信息:

  • 骑士在每个房间至少有一点健康点,这样就不会死亡
  • 每次移动只能向右或向下移动一步
  • 确保骑士救出公主,遍历路线,找到最小值路线

这里有两种推导方式,一种是从前往后推,但是并不知道一开始的值是多少,假设开始设置了一个值,在推导过程中发现不满足条件就需要修改开始的值,这样很麻烦。

所以可以从后往前推,每个房间找到最优解,到起点就是要求的路线。

就比如这个例子:

1702379787791.jpg

从右下出发,那么往左走(5),往上走(2):

1702379796741.jpg

那么很显然向上是最优解,那么向上后,再继续往左走(2),或往上走(-1):

1702379810800.jpg

那么显然往上走是最优解,然后骑士健康点数要大于等于1,那么往上走的点数其实是1:

1702379821702.jpg

这样是不是就求出了一条可行的最优解了。

整理一下这个公式,对于这条路径dp[i][j]来说,只要求出dp[i][j+1]和dp[i+1][j]的最小值minn,当前格式的值为dungeon(i,j),那么在坐标(i,j)的初始值只要达到minn-dungeon(i,j)即可,同时,初始值还必须大于等于1,那么方程:

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

边界条件是当 i = n-1或者 j=m -1时,dp[i][j]转移需要用到dp[i][j+1]和dp[i+1][j]中有无效值,因此代码实现中给无效值赋值为极大值。

2、代码实现

代码参考:

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int n = dungeon.length, m = dungeon[0].length;
        int[][] dp = new int[n + 1][m + 1];
        for (int i = 0; i <= n; ++i) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        dp[n][m - 1] = dp[n - 1][m] = 1;
        for (int i = n - 1; i >= 0; --i) {
            for (int j = m - 1; j >= 0; --j) {
                int minn = Math.min(dp[i + 1][j], dp[i][j + 1]);
                dp[i][j] = Math.max(minn - dungeon[i][j], 1);
            }
        }
        return dp[0][0];
    }
}

1702379840533.jpg

3、时间复杂度

时间复杂度:O(N X M)

其中N,M是给定矩阵的长宽。

空间复杂度:O(N X M)

其中N,M是给定矩阵的长宽。

三、总结

之所以要从终点向起点出发,是因为从起点到终点破坏了无后效性。

什么是无后效性,就是无法直接确定这条路径是否是唯一解,因为有两个重要程度相同的参数同时影响后续的决策。

这两个参数分别是:剩余血量与到达当前点所需要的最小初始化血量共同影响着后面一步的结果。

相关文章
|
9月前
|
算法 Go 索引
【LeetCode 热题100】45:跳跃游戏 II(详细解析)(Go语言版)
本文详细解析了力扣第45题“跳跃游戏II”的三种解法:贪心算法、动态规划和反向贪心。贪心算法通过选择每一步能跳到的最远位置,实现O(n)时间复杂度与O(1)空间复杂度,是面试首选;动态规划以自底向上的方式构建状态转移方程,适合初学者理解但效率较低;反向贪心从终点逆向寻找最优跳点,逻辑清晰但性能欠佳。文章对比了各方法的优劣,并提供了Go语言代码实现,助你掌握最小跳跃次数问题的核心技巧。
372 15
|
3月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
4月前
|
机器学习/深度学习 人工智能 算法
当AI提示词遇见精密算法:TimeGuessr如何用数学魔法打造文化游戏新体验
TimeGuessr融合AI与历史文化,首创时间与空间双维度评分体系,结合分段惩罚、Haversine距离计算与加权算法,辅以连击、速度与完美奖励机制,实现公平且富挑战性的游戏体验。
|
9月前
|
机器学习/深度学习 存储 算法
【LeetCode 热题100】347:前 K 个高频元素(详细解析)(Go语言版)
这篇文章详细解析了力扣热题 347——前 K 个高频元素的三种解法:哈希表+小顶堆、哈希表+快速排序和哈希表+桶排序。每种方法都附有清晰的思路讲解和 Go 语言代码实现。小顶堆方法时间复杂度为 O(n log k),适合处理大规模数据;快速排序方法时间复杂度为 O(n log n),适用于数据量较小的场景;桶排序方法在特定条件下能达到线性时间复杂度 O(n)。文章通过对比分析,帮助读者根据实际需求选择最优解法,并提供了完整的代码示例,是一篇非常实用的算法学习资料。
573 90
|
算法
Leetcode第45题(跳跃游戏II)
这篇博客文章讨论了如何使用贪心算法解决LeetCode第45题“跳跃游戏II”,目的是找到使用最少跳跃次数到达数组末尾的策略。
275 8
Leetcode第45题(跳跃游戏II)
|
10月前
|
存储 自然语言处理 算法
【LeetCode 热题100】208:实现 Trie (前缀树)(详细解析)(Go语言版)
本文详细解析了力扣热题 208——实现 Trie(前缀树)。Trie 是一种高效的树形数据结构,用于存储和检索字符串集合。文章通过插入、查找和前缀匹配三个核心操作,结合 Go 语言实现代码,清晰展示了 Trie 的工作原理。时间复杂度为 O(m),空间复杂度也为 O(m),其中 m 为字符串长度。此外,还探讨了 Trie 的变种及应用场景,如自动补全和词典查找等。适合初学者深入了解 Trie 结构及其实际用途。
331 14
|
9月前
|
算法 Go
【LeetCode 热题100】55:跳跃游戏(详细解析)(Go语言版)
本篇解析详细讲解了 LeetCode 热题 55——跳跃游戏(Jump Game)。通过判断是否能从数组起点跳至终点,介绍了两种高效解法:贪心算法和反向思维。贪心法通过维护最远可达位置 `maxReach` 实现一次遍历,时间复杂度 O(n),空间复杂度 O(1);反向法则从终点回溯,判断是否可到达起点。两者均简洁高效,适合面试使用。延伸题目如 LeetCode 45 进一步提升挑战。
298 7
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
162 0
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
332 4
|
算法
测试工程师的技能升级:LeetCode算法挑战与职业成长
这篇文章通过作者亲身体验LeetCode算法题的过程,探讨了测试工程师学习算法的重要性,并强调了算法技能对于测试职业成长的必要性。
285 1
测试工程师的技能升级:LeetCode算法挑战与职业成长

推荐镜像

更多
  • DNS