Java刷题笔记12:地下城游戏

题目描述

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

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

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

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

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

示例

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

2(k)-33
-5-101
1030-5(终点)

题目链接

动态规划

为了求出初始点到终点所需的最小初始生命值,可以采取动态规划的方式求解。对于此题似乎有两种可行的动态规划方案

  • 方案1:在dp[i][j]里保存初始点到坐标(i,j)的最小初始生命值。
  • 方案2:在dp[i][j]里保存坐标(i,j)到终点所需的最小初始生命值。

第一种方案中dp[n-1][m-1]即为初始点到终点所需的最小初始生命值,第二种方案中dp[0][0]为初始点到终点所需的最小初始生命值。

然而第一种方案有着重大的缺陷,那就是不符合动态规划的无后效性。对于示例中的案例。为了到达dungeon[0,1],k的初始值因大于等于2(初始值+dungeon[0][0]+dungeon[0][1]>=1,解为初始值>=2)即dp[0][1]=2,同理为了到达dungeon[1][0],初始值最小为4,即dp[1][0]=4。接着为了到达dungeon[1][1],在dungeon[0][1]以及dungeon[1][0]里的生命值最少得为11(这样才能进入dungeon[1][1]后保持最低生命值1),此时它的最小初始生命值就必须随之提高。显然,这不满足dp的无后效性。所以方案一只能废弃。

对于方案二,我们可以从右下角至左上角开始dp。对于每一个房间,我们需要保持的生命值最低为1,所以进入房间时,我们的初始生命值+dungeon[i][j]必须大于等于1.如示例中,达到房间dp[2][2]时,当前的初始生命值至少为6,即dp[2][2]=6,这样-5之后才能保持存活。在进入下一个放间时,我们只需要选取需要初始生命值最少的房间进入即可,且进入当前房间的初始生命值+dungeon[i][j]要大于等于下一个房间所需要的最低初始生命值。所以有dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i][j].,当这样计算出的生命值小于1时我们将其赋值为1.(这个房间会恢复大量生命值,导致这一个房间所需要的最低初始生命值为0或负数,而为了保持存活,生命值最低为1)。所以最终的动态转移方程为: dp[i][j]=Math.max(1,Math.min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]);
对于边界dp[n-1][m-1]=Math.max(1,1-dungeon[n-1][m-1]);

public int calculateMinimumHP(int[][] dungeon) {
        //dp[i][j]保存进入房间(i,j)到终点所需要的最低生命值,且最低为1(保持存活状态).
        //从右下角至左上角。
        int n=dungeon.length;
        int m=dungeon[0].length;
        int dp[][] =new int [n][m];
        //初始化边界
        dp[n-1][m-1]=Math.max(1,1-dungeon[n-1][m-1]);
        for(int i=n-2;i>=0;i--){
            dp[i][m-1]=Math.max(1,dp[i+1][m-1]-dungeon[i][m-1]);
        }
        for(int i=m-2;i>=0;i--){
            dp[n-1][i]=Math.max(1,dp[n-1][i+1]-dungeon[n-1][i]);
        }
        //正式开始dp
        for(int i=n-2;i>=0;i--){
            for(int j=m-2;j>=0;j--){
                dp[i][j]=Math.max(1,Math.min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]);
            }
        }
        return dp[0][0];
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值