力扣刷题笔记174. 地下城游戏 C#

今日签到题,题目如下:

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

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

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

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

 

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

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

-2 (K)    -3    3
-5    -10    1
10    30    -5 (P)
 

说明:

骑士的健康点数没有上限。

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dungeon-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

看到题目真的是笑了,和前几天的签到题 63. 不同路径 II 几乎如出一辙,本题每个格子的怪物和血包相当于添加了一个移动代价,即求解起点到终点的最小代价。感觉这题目算不上困难级别,也可能是我之前的项目中写过一些寻路之类的功能,所以这么觉得。

还是使用动态规划。

设地图数组 dungeon 长宽分别为 m 和 n,建立长度为 m*n 的二维整型数组 dp,记录对应的每个地图格子到终点的最小代价。

由于骑士只会向下或者向右移动一格,所以 dp[i][j] 状态转移自 dp[i - 1][j] 和 dp[i][j - 1] 中的较小值,同时还要击计算 dungeon[i][j] 的消耗。

  1. 如果为空,则不操作,只做状态转移(代码中我没有和其他两种情况区分,结果一样)。
  2. 如果是怪物,则加上 -dungeon[i][j],因为在到达 dungeon[i][j] 之前要有足够 dungeon[i][j] 消耗的血量,即 dp[i][j] -= dungeon[i][j]。
  3. 如果是魔法球,那么要减去 dungeon[i][j],因为能够补给,所以计算 dungeon[i][j] 之后路径消耗之前有补给 dungeon[i][j] 的血量,即 dp[i][j] -= dungeon[i][j]。

1 和 2 的表达式相同,都为 dp[i][j] -= dungeon[i][j]。但是,因为骑士血量如果少于 1 则死亡,所以如果 dp[i][j] <= 0,dp[i][j] = 1。

因为状态从终点开始转移,所以我们采用逆序遍历。

考虑边界条件。

终点状态自来源于终点的血量消耗,所以先计算终点的 dp 。骑士要存活到终点,即计算完终点血量消耗后,血量最小值应该为 1,题解求最小值。所以如果终点是怪物,则到达终点的血量应该是 -dungeon[m-1][n-1] + 1,如果不扣血或者加血,那么到达终点的血量就应该为 1。

dp[i,n-1] 和 dp[m-1,j] 都分别指转移自右边和下边的状态,也遍历一遍获得 dp 值。

计算完边界后,两层的嵌套循环从 m-2 和 n-2 开始就好了。

复杂度分析:

实质为对整个 dungeon 进行枚举,时间复杂度为 O(M*N)。

建立长度 M*N 的数组 dp,空间复杂度为 O(M*N)。有看到其他大佬提到可以用滑动数组优化空间,即可以空间复杂度可以优化为 O(N)。

以下为自己提交的代码:
 

public class Solution {
    public int CalculateMinimumHP(int[][] dungeon) {
        int m = dungeon.Length;
        int n = dungeon[0].Length;
        int[,] dp = new int[m,n];
        dp[m-1,n-1] = dungeon[m-1][n-1] >= 0 ? 1: -dungeon[m-1][n-1] + 1;
        for (int i = m - 2; i >= 0; i--)
        {
            int ans = dp[i+1,n-1] - dungeon[i][n-1];
            if (ans <=0) ans = 1;
            dp[i,n-1] = ans;
        }
        for (int j = n - 2; j >= 0; j--)
        {
            int ans = dp[m-1,j+1] - dungeon[m-1][j];
            if (ans <= 0) ans = 1;
            dp[m-1,j] = ans;
        }
        for (int i = m - 2; i >= 0; i--)
        {
            for (int j = n - 2; j >= 0; j--)
            {
                int ans = 0;
                if (dp[i,j+1] > dp[i+1,j])
                {
                    ans = dp[i + 1,j] - dungeon[i][j];
                }
                else
                {
                    ans = dp[i,j+1] - dungeon[i][j];
                }
                if (ans <= 0) ans = 1;
                dp[i,j] = ans;
            }
        }
        return dp[0,0];
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值