当前位置: 首页 > news >正文

动态规划-地下城游戏

在上一篇博客中,我们了解两个关于路径的问题,在这一篇中,我们将进一步了解一道更加困难的问题

题目描述

恶魔们抓住了公主并将她关在了地下城的右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。 为了尽快解救公主,骑士决定每次只 向右向下 移动一步。 返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间

174. 地下城游戏 - 力扣(LeetCode)

解题思路

在这个问题中,我们定义dp[i][j]为骑士从网格中的(i, j)位置(即第i行第j列的房间)出发,到达终点(右下角)所需的最低初始健康点数。

初始化

  • 首先,我们需要考虑边界情况。由于骑士必须从左上角出发,因此dp[0][0]应该根据dungeon[0][0]的值进行调整。如果dungeon[0][0]是负的,那么骑士至少需要-dungeon[0][0] + 1的健康点数来确保从起点出发时不会立即死亡。如果dungeon[0][0]是非负的,那么骑士的初始健康点数可以设为1(因为已经足够应对该房间的情况)。

  • 对于其他边界位置(第一行和第一列),我们需要确保骑士在到达这些位置时不会死亡,并计算到达这些位置所需的最低健康点数。这通常涉及向前看(向右或向下),以确定从当前位置到边界的下一个安全点所需的健康点数。

状态转移方程

对于网格中的非边界位置(i, j),我们可以根据以下规则计算dp[i][j]

  • 如果骑士从(i, j)向右移动到(i, j+1)是安全的(即dp[i][j+1]存在且不会导致死亡),则dp[i][j]可以是min(1, dp[i][j+1] - dungeon[i][j])。这里的1表示骑士至少需要1点健康点数才能存活,而dp[i][j+1] - dungeon[i][j]表示从(i, j)向右移动后到达(i, j+1)时所需的健康点数。

  • 如果骑士从(i, j)向下移动到(i+1, j)是安全的,则类似地,dp[i][j]可以是min(1, dp[i+1][j] - dungeon[i][j])

  • 但是,骑士不能同时向右和向下移动,因此我们需要取上述两种情况中的较大值,以确保无论选择哪个方向,骑士都有足够的健康点数。即:

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

    注意,如果dungeon[i][j]是一个很大的正数,那么骑士在到达(i, j)后可能会拥有超过dp[i][j+1]dp[i+1][j]的健康点数。然而,我们关心的是确保骑士在到达(i, j)时至少拥有1点健康点数,并且有足够的健康点数来应对接下来的移动。

特殊情况处理

  • 当骑士接近右下角时,他/她需要确保有足够的健康点数来应对最后一个房间(即公主所在的房间)。因此,在计算dp[m-1][n-1]时,我们需要确保dp[m-1][n-1] - dungeon[m-1][n-1] >= 1,即使这意味着dp[m-1][n-1]需要被设置为一个相对较大的值。

通过这种方法,我们可以从右下角开始,逆向填充dp数组,直到左上角。最终,dp[0][0]将包含骑士从左上角出发,确保能够拯救公主所需的最低初始健康点数。

class Solution {
public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m = dungeon.size();int n = dungeon[0].size();// 创建一个DP数组,初始化为0vector<vector<int>> dp(m, vector<int>(n, 0));dp[m - 1][n - 1] = max(1, 1 - dungeon[m - 1][n - 1]); // 右下角特殊处理// 初始化最后一列for (int i = m - 2; i >= 0; i--) {dp[i][n - 1] = max(1, dp[i + 1][n - 1] - dungeon[i][n - 1]);}// 初始化最后一行for (int j = n - 2; j >= 0; j--) {dp[m - 1][j] = max(1, dp[m - 1][j + 1] - dungeon[m - 1][j]);}// 填充DP数组的其他部分for (int i = m - 2; i >= 0; i--) {for (int j = n - 2; j >= 0; j--) {dp[i][j] =max(1, min(dp[i][j + 1], dp[i + 1][j]) - dungeon[i][j]);}}return dp[0][0];}
};

代码解释

  1. 我们首先定义了一个dungeon的二维向量来表示地下城,并初始化了DP数组dp

  2. 我们从右下角开始逆向计算DP数组。因为右下角是终点,所以我们可以直接根据dungeon[m-1][n-1]的值来设置dp[m-1][n-1]

  3. 然后,我们分别初始化DP数组的最后一列和最后一行。这是因为当我们处于这些边界上时,我们只能沿着一个方向移动(向右或向下),所以计算相对简单。

  4. 接下来,我们填充DP数组的其他部分。对于每个位置(i, j),我们查看其右侧和下方的位置,并选择其中所需健康点数较小的那个。但是,我们还需要确保这个值至少为1,因为骑士至少需要1点健康点数才能存活。

  5. 最后,我们返回dp[0][0],即从左上角出发所需的最低初始健康点数。


http://www.mrgr.cn/news/10615.html

相关文章:

  • Elasticsearch之DSL查询语法
  • 高级java每日一道面试题-2024年8月21日-框架篇[Spring篇]-使用IOC容器应该注意哪些?
  • Qt:玩转QPainter序列一
  • dokcer 安装 redis(单机版)
  • Python将Word文档转为PDF
  • 基于SpringBoot+Vue的宿舍管理系统
  • Vulkan入门系列16 - 生成多级纹理贴图( Mipmaps)
  • 【网络安全】缓存配置错误导致授权绕过
  • 基础训练 (待补充)
  • Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单
  • 【学习笔记】Day 22
  • 远程在电脑上玩PS5《黑神话:悟空》?借助极空间实现PS5远程串流攻略
  • 【nextjs strapi】如何统一封装 fetch 请求
  • 查找算法刷题【哈希表算法】
  • 6-1 STM32F405--DAC输出(软件触发)
  • 面试题Java版(含大厂校招面试题)
  • Java中的单例模式
  • zookeeper 集群搭建 及启动关闭脚本
  • Python OpenCV 影像处理:影像轮廓
  • LlamaIndex 实现 RAG(四)- RAG 跟踪监控