码迷,mamicode.com
首页 > 编程语言 > 详细

【算法】地下城与勇士

时间:2020-05-24 13:27:07      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:style   影响   dungeon   比较   lse   width   初始   贪心算法   进入   

题目

一些恶魔抓住了公主(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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题

看到这种求全局最值+状态转移的题目,就想到了动态规划。

给出的数组为dungeon,我自设的数组为dp保存每个位置的最低可存活健康值。

一开始想到了从起点开始动态规划,类似贪心算法求最优路径,举个栗子:

dp[0][0]时的最低健康值为3(因为任何时候不可降至0及以下,至少为3才能在-2后仍大于0);

dp[1][0],只能从上侧来,因此dp[0][0]-dungeon[1][0]=3-(-5)=8(在这个格子的时候至少为8,才能经得住-5这样的消耗,且保证上个格子的时候也是活着的);

dp[2][0]时,只能从上侧来,因此为8-10=-2小于0了,该格子可提供的健康值大于上一步时的最低可存活健康值了,如果dp[2][0]=dp[1][0],那么如果后续格子有需要大量消耗的话,明明前面提供的健康值能补充上使用,可是dp[2][0]=dp[1][0]+dungeon[2][0]的话,后面的格子如果都没有消耗了,那么dp[2][0]就没有加上这么多健康值,这个问题就是无后效性问题,没办法预见后面的情况。

像这种前面的决策会影响到后面的情况,就可以使用从后往前的办法动态规划。

 

 1 public int calculateMinimumHP(int[][] dungeon) {
 2         int row=dungeon.length;
 3         int col=dungeon[0].length;
 4         //这个数组表示在i,j位置骑士需要的最小生命值
 5         int[][] dp=new int[row][col];
 6         for(int i=row-1;i>=0;i--){
 7             for(int j=col-1;j>=0;j--){
 8                 if(i==row-1&&j==col-1){ //终点的情况
 9                     dp[i][j]=Math.max(1, 1-dungeon[i][j]);
10                 }else if(i==row-1){ //最后一行的情况
11                     dp[i][j]=Math.max(1, dp[i][j+1]-dungeon[i][j]);
12                 }else if(j==col-1){ //最后一列的情况
13                     dp[i][j]=Math.max(1, dp[i+1][j]-dungeon[i][j]);
14                 }else{    
15                     dp[i][j]=Math.max(1, Math.min(dp[i+1][j],dp[i][j+1])-dungeon[i][j]);
16                 }
17             }
18         }
19         return dp[0][0];
20     }

解释一下几个式子:

在终点时:

dp[i][j]=Math.max(1, 1-dungeon[i][j]);如果dungeon>1,说明终点位置能提供给骑士健康值让骑士,那么刚到这个位置时骑士保证自己活着就行了,dp为1;如果dungeon=0或<0,则终点不能提供给骑士生命,甚至还要消耗生命,那么骑士不仅要活着到这个位置,还要经得住在这个位置上的消耗,则为1-dungeon,例如dungeon为-9的话那么骑士的健康值至少为10.

最后一行和最后一列:

最后一行时骑士只能从右边走过来,骑士保存着上一步(从终点方向来的)的健康值,

①如果当前位置为负,则dp[i][j]=dp[i][j+1]-dungeon[i][j],也就是准备上将要被耗掉的健康值才能保证骑士活着,

②如果当前位置为0,则dp[i][j+1]-dungeon[i][j]=dp[i][j+1],骑士保持上一步的健康值就能活,

如果当前位置为正,说明当前位置能提供给骑士健康值,dp[i][j+1]-dungeon[i][j]>=1时当前位置虽然能提供一些但是不能提供所有dp[i][j+1]需要的,dp[i][j+1]-dungeon[i][j]<1时当前位置能提供所有所需的健康值,即dp[i][j+1]需要的可以完全从dp[i][j]获取,骑士只需要准备1的健康值让自己活着就好,之后再到终点都不必担心。最后一列时骑士只能从下面走过来,情况类似。

其它一般情况:

每个位置都可以从右边和下边走来,因此比较右边和下边位置的dp值取最小的路走,减去dungeon,若大于1说明当前位置要消耗健康值或者能提供少量健康值,若小于1说明当前位置能提供非常多的健康值则取1即可。

【算法】地下城与勇士

标签:style   影响   dungeon   比较   lse   width   初始   贪心算法   进入   

原文地址:https://www.cnblogs.com/zhangmora/p/12950594.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!