【Java】LeetCode 174. 地下城游戏 —— 困难

一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快到达公主,骑士决定每次只向右或向下移动一步。
编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
-2 (K) -3 3
-5 -10 1
10 30 -5 §
说明:
骑士的健康点数没有上限。
任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/dungeon-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

首先想到暴力解法,遍历每一条可能的路径,但是由于这个问题是指数型问题,暴力解法显然会因过于消耗资源以至于超时

那如果用空间换时间,维护一个同样大小的二维数组,用来存储“从起点到此处,最少扣多少血”呢?

乍一想好像很美好

但是请假设,有一条路径是:-100,-100,200,-1,-1

如果用上面的方法来计算,似乎整一条路径只要2点血,初始3血即可

可是,真实情况却是,我们的勇士,从第一个房间进去就GG了

为什么呢?

加血只能用来打后面的怪,你又不能欠血然后进加血房换回来(您以为血量这玩意还能打白条呢?)

这样的话,从起点出发似乎就行不通了

反过来想,如果从公主房往回退呢?

那么每一个数组元素应该存的是“从进这个之前房间一直到进入公主房间,我的血量要多少,才能保证我的血量不至于到0”

那么,怎么算呢?

基于我们只能往右或者往下走,也就是说,对于房间[i][j]来说,可以只关注[i+1][j]和[i][j+1]以及自己本身的值(打怪还是加血),从[i+1][j]和[i][j+1]出发所需要的最小血量,应该在之前已经计算出来

ij房间来说,如果这个房间是打怪,很简单,只要[i+1][j]和[i][j+1]中,较小值,加上ij房间中怪物血量就可以了

如果是加血呢,减少对应血量就行,但是要注意!再怎么减少,不能减少到0,否则可没有英雄不朽,这时就强制血量为1


整个过程大概如图

当然我实现的时候是先把最后一列和最后一行先算出来,然后再进行其余需要判断两边路径大小的位置,原理是一样的

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        int a=dungeon.length,b=dungeon[0].length;
        int[][] hp=new int[a][b];
        //公主房间
        hp[a-1][b-1]=Math.max(1,-dungeon[a-1][b-1]+1);
        //计算最后一列
        for(int i=a-2;i>=0;i--){
            hp[i][b-1]=Math.max(1,hp[i+1][b-1]-dungeon[i][b-1]);
        }
        //计算最后一行
        for(int i=b-2;i>=0;i--){
            hp[a-1][i]=Math.max(1,hp[a-1][i+1]-dungeon[a-1][i]);
        }
        //计算剩下的
        for(int i=a-2;i>=0;i--){
            for(int j=b-2;j>=0;j--){
                hp[i][j]=Math.min(Math.max(1,hp[i+1][j]-dungeon[i][j]),Math.max(1,hp[i][j+1]-dungeon[i][j]));
            }
        }
        //返回初始房间血量
        return hp[0][0];
    }
}

当然由于这个算法是可以逐行或者逐列扫描的,甚至可以只存储单列或者单行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值