力扣——174.地下城游戏(困难难度)——万能的递归与动态分析

力扣——174. 地下城游戏
一、算法目录合集1.地址2.说明
二、题目说明1.题干2.原地址
三、实现步骤1.思路分析1.1.分析问题1.2.具体步骤① 特殊情况分析② 常规分析

2.代码实现2.1 方法代码2.2 测试部分代码2.3 耗用资源情况

四、官方题解1.原地址2.方法一——动态规划思路分析代码实现(Java)复杂度

一、算法目录合集
1.地址

   算法目录合集

2.说明

  该地址指向所有由本人自己所经历的算法习题(也有可能仅仅是一个入门的案例或者是经典案例),仅仅为我做过而且比较有意思的,也许还会有一些我自己想出来的,出于兴趣写在这里,具体格式我会在下面列明,总目录也会在这里出现,方便查阅以及自己进行复习回顾。

二、题目说明
1.题干

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

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

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

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

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

  例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
力扣——174.地下城游戏(困难难度)——万能的递归与动态分析

  说明:

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

  174. 地下城游戏

三、实现步骤
1.思路分析
1.1.分析问题

  对于这个题,有一种常规的方法——动态分析(一看这不就是动态分析😀),因为血量是由两个条件决定的(每个房间不能死掉,并且到终点时血量最少),所以正着去分析时有难度,不如讲血量定为1,反向分析,也就是说:当我们到达坐标

(

i

,

j

)

(i,j)

(i,j)时,如果此时我们的路径和不小于

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] ,我们就能到达终点。 既然要反向分析了,那我就不展示动态分析了,动态分析官方题解也有,我就用一个一提反向就能想起来的词儿去做:递归。没错,我已经在前几篇文章讲过递归的一些题了,这其实也可以递归求解。

  首先要分析一下,如何递归?
  关注一下上面一段的黄线,就假设前面已经操作完了,那么现在我们只需要选取向右或者向下,取一个

m

i

n

min

min就可以了,直接调用

M

a

t

h

.

m

i

n

(

)

Math.min()

Math.min()方法就行了,如果这时候我们的现存血量减去本格子的耗血量(负值也适用)大于零,就说明或者,那么便可以让血量变成1(既然多少血都能或者,那么1可以使血量保持最低),如果小于零,说明之前的血量不够用,那么剩余血量必须得是最小值

m

i

n

min

min减去本格的耗血量。

  就这样一直递归回第一步,返回即可。

1.2.具体步骤
① 特殊情况分析

  如果遇到了边界,则返回int的最大值过滤,之所以不写999或者9999,是因为你也不知道一个格子究竟扣多少血,万一给你来个几万呢😄所以来个Integer.MAX_VALUE稳妥一些。

if (m >= dungeon.length || n >= dungeon[0].length) {
return Integer.MAX_VALUE;
}

  如果找到了公主,则返回所就到公主之前的时候的血量最小值,把值返回,以供调用者使用。

if (m == dungeon.length - 1 && n == dungeon[0].length - 1) {
return dungeon[m][n] >= 0 ? 1 : 1 - dungeon[m][n];
}

  如果本位置为0,说明dp还没有被赋值,此项判断是为了把指针放在公主的位置,并往回寻找。

if (dp[m][n] != 0) {
return dp[m][n];
}

② 常规分析

  常规步骤就是比较简单的了,选取两种分支的最小值,并将其与本格子的耗血量进行比较,来为dp数组赋值储存,最终返回

d

p

[

0

]

[

0

]

dp[0][0]

dp[0][0]即可。

int min = Math.min(blood(dp,dungeon, m + 1, n), blood(dp,dungeon, m, n + 1));
dp[m][n] = dungeon[m][n] - min >= 0 ? 1 : min - dungeon[m][n];
return dp[m][n];

2.代码实现
2.1 方法代码

public class Solution174 {
public int calculateMinimumHP(int[][] dungeon) {
int[][] dp = new int[dungeon.length][dungeon[0].length];
return blood(dp, dungeon, 0, 0);
}

public int blood(int[][] dp, int[][] dungeon, int m, int n) {
if (m >= dungeon.length || n >= dungeon[0].length) {
return Integer.MAX_VALUE;
}
if (m == dungeon.length - 1 && n == dungeon[0].length - 1) {
return dungeon[m][n] >= 0 ? 1 : 1 - dungeon[m][n];
}
if (dp[m][n] != 0) {
return dp[m][n];
}
int min = Math.min(blood(dp,dungeon, m + 1, n), blood(dp,dungeon, m, n + 1));
dp[m][n] = dungeon[m][n] - min >= 0 ? 1 : min - dungeon[m][n];
return dp[m][n];
}
}

2.2 测试部分代码

  这里随便定义一个随便看看就好了

public class Test174Hard {
public static void main(String[] args) {
Solution174 s = new Solution174();
int i1 = s.calculateMinimumHP(new int[][]{{1, 2, 3}, {0, -5, 0}, {-2, 1, 1}});
int i2 = s.calculateMinimumHP(new int[][]{{-2,-3,3},{-5,-10,1},{10,30,-5}});
System.out.println(i1);
System.out.println(i2);
}
}

  测试结果

1
7

2.3 耗用资源情况

力扣——174.地下城游戏(困难难度)——万能的递归与动态分析

四、官方题解
1.原地址

力扣官方答疑戳这里

2.方法一——动态规划
思路分析

思路及算法
  几个要素:「

M

×

N

M×N

M×N 的网格」「每次只能向右或者向下移动一步」。让人很容易想到该题使用动态规划的方法。
  但是我们发现,如果按照从左上往右下的顺序进行动态规划,对于每一条路径,我们需要同时记录两个值。第一个是「从出发点到当前点的路径和」,第二个是「从出发点到当前点所需的最小初始值」。而这两个值的重要程度相同,参看下面的示例:
力扣——174.地下城游戏(困难难度)——万能的递归与动态分析
  从

(

0

,

0

)

(0,0)

(0,0)到

(

1

,

2

)

(1,2)

(1,2) 有多条路径,我们取其中最有代表性的两条:
力扣——174.地下城游戏(困难难度)——万能的递归与动态分析

绿色路径「从出发点到当前点的路径和」为

1

1

1,「从出发点到当前点所需的最小初始值」为

3

3

3。蓝色路径「从出发点到当前点的路径和」为

1

−1

−1,「从出发点到当前点所需的最小初始值」为

2

2

2。

  
  我们希望「从出发点到当前点的路径和」尽可能大,而「从出发点到当前点所需的最小初始值」尽可能小。这两条路径各有优劣。
  在上图中,我们知道应该选取绿色路径,因为蓝色路径的路径和太小,使得蓝色路径需要增大初始值到

4

4

4 才能走到终点,而绿色路径只要

3

3

3 点初始值就可以直接走到终点。但是如果把终点的

2

-2

−2 换为

0

0

0,蓝色路径只需要初始值

2

2

2,绿色路径仍然需要初始值

3

3

3,最优决策就变成蓝色路径了。
  因此,如果按照从左上往右下的顺序进行动态规划,我们无法直接确定到达

(

1

,

2

)

(1,2)

(1,2) 的方案,因为有两个重要程度相同的参数同时影响后续的决策。也就是说,这样的动态规划是不满足「无后效性」的。
  于是我们考虑从右下往左上进行动态规划。令

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] 表示从坐标

(

i

,

j

)

(i,j)

(i,j) 到终点所需的最小初始值。换句话说,当我们到达坐标

(

i

,

j

)

(i,j)

(i,j)时,如果此时我们的路径和不小于

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] ,我们就能到达终点。
  这样一来,我们就无需担心路径和的问题,只需要关注最小初始值。对于

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] ,我们只要关心

d

p

[

i

]

[

j

+

1

]

dp[i][j + 1]

dp[i][j+1] 和

d

p

[

i

+

1

]

[

j

]

dp[i + 1][j]

dp[i+1][j] 的最小值

m

i

n

n

minn

minn。记当前格子的值为

d

u

n

g

e

o

n

(

i

,

j

)

dungeon(i,j)

dungeon(i,j),那么在坐标

(

i

,

j

)

(i,j)

(i,j) 的初始值只要达到

m

i

n

n

d

u

n

g

e

o

n

(

i

,

j

)

minn−dungeon(i,j)

minn−dungeon(i,j) 即可。同时,初始值还必须大于等于

1

1

1。这样我们就可以得到状态转移方程:
  

d

p

[

i

]

[

j

]

=

m

a

x

(

m

i

n

(

d

p

[

i

+

1

]

[

j

]

,

d

p

[

i

]

[

j

+

1

]

)

d

u

n

g

e

o

n

(

i

,

j

)

,

1

)

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

dp[i][j]=max(min(dp[i+1][j],dp[i][j+1])−dungeon(i,j),1)
  最终答案即为

d

p

[

0

]

[

0

]

dp[0][0]

dp[0][0]。
  边界条件为,当

i

=

n

1

i=n−1

i=n−1 或者

j

=

m

1

j=m-1

j=m−1 时,

d

p

[

i

]

[

j

]

dp[i][j]

dp[i][j] 转移需要用到的

d

p

[

i

]

[

j

+

1

]

dp[i][j+1]

dp[i][j+1] 和

d

p

[

i

+

1

]

[

j

]

dp[i+1][j]

dp[i+1][j] 中有无效值,因此代码实现中给无效值赋值为极大值。特别地,

d

p

[

n

1

]

[

m

1

]

dp[n−1][m−1]

dp[n−1][m−1] 转移需要用到的

d

p

[

n

1

]

[

m

]

dp[n−1][m]

dp[n−1][m] 和

d

p

[

n

]

[

m

1

]

dp[n][m-1]

dp[n][m−1] 均为无效值,因此我们给这两个值赋值为

1

1

1。

代码实现(Java)

class Solution {
public int calculateMinimumHP(int[][] dungeon) {
int n = dungeon.length, m = dungeon[0].length;
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= n; ++i) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
dp[n][m - 1] = dp[n - 1][m] = 1;
for (int i = n - 1; i >= 0; --i) {
for (int j = m - 1; j >= 0; --j) {
int minn = Math.min(dp[i + 1][j], dp[i][j + 1]);
dp[i][j] = Math.max(minn - dungeon[i][j], 1);
}
}
return dp[0][0];
}
}

复杂度
时间复杂度:
  

O

(

N

×

M

)

O(N×M)

O(N×M),其中

N

N

N,

M

M

M 为给定矩阵的长宽。空间复杂度:
  

O

(

N

×

M

)

O(N×M)

O(N×M),其中

N

N

N,

M

M

M 为给定矩阵的长宽,注意这里可以利用滚动数组进行优化,优化后空间复杂度可以达到

O

(

N

)

O(N)

O(N)。

原创:https://www.panoramacn.com
源码网提供WordPress源码,帝国CMS源码discuz源码,微信小程序,小说源码,杰奇源码,thinkphp源码,ecshop模板源码,微擎模板源码,dede源码,织梦源码等。

专业搭建小说网站,小说程序,杰奇系列,微信小说系列,app系列小说

力扣——174.地下城游戏(困难难度)——万能的递归与动态分析

免责声明,若由于商用引起版权纠纷,一切责任均由使用者承担。

您必须遵守我们的协议,如果您下载了该资源行为将被视为对《免责声明》全部内容的认可-> 联系客服 投诉资源
www.panoramacn.com资源全部来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。 敬请谅解! 侵权删帖/违法举报/投稿等事物联系邮箱:2640602276@qq.com
未经允许不得转载:书荒源码源码网每日更新网站源码模板! » 力扣——174.地下城游戏(困难难度)——万能的递归与动态分析
关注我们小说电影免费看
关注我们,获取更多的全网素材资源,有趣有料!
120000+人已关注
分享到:
赞(0) 打赏

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

您的打赏就是我分享的动力!

支付宝扫一扫打赏

微信扫一扫打赏