2024年动态规划之地下城游戏(1),面试经验分享app

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

此时 X = dp[i][j+1] - dungeon[i][j]

  1. 从 ( i, j ) 位置出发往 ( i+1, j ) 位置走

同理, 从 ( i, j ) 位置出发往 ( i+1, j ) 想要到达终点所需要的最低初始血量为 dp[i+1][j] - dungeon[i][j]

由于我们求的是从某点出发到终点的最低初始血量, 最终 dp[i][j] = Math.min(dp[i][j+1], dp[i+1][j] ) - dungeon[i][j]

PS :有一点是需要注意的, 当 dungeon[i][j] 非常大的时候, dp[i][j] 最终是一个小于 0 的数. 这是什么意思 ?

也就是当你进入 ( i, j ) 位置时, 这里面是一个非常大的血包 dungeon[i][j] 此时我进入 ( i, j ) 位置时自身血量哪怕是负数最终也可以进入到 ( i, j+1 ) 位置. 这是不合理的, 负数就应该直接死亡. 必须保证在进入 ( i, j ) 位置时它的血量大于 0 才行.

因此我们需要对最终的 dp[i][j] 进行处理. dp[i][j] = Math.max( 1, dp[i][j] ). 也就是进入某处时的最低初始血量必须是大于 0 才能进入 !

4. 初始化

根据状态转移方程, 想要计算从 ( i, j ) 位置出发到达终点时的最低初始血量, 就需要先计算它的前一个位置和下一个位置的最低初始血量. 因此以下位置在填表时不进行初始化就会导致填表发生越界错误.
image.png
因此, 这些位置在进行填表之前, 都是我们需要进行初始化的. 同样, 我们还是采取新增一行一列的办法, 将初始化的内容放在填表当中. 但此时不是和之前一样, 左边添加一列上面添加一行, 而是下面添加一行最后面添加一列
image.png

  1. 当填写这个位置的值时

由于它受到向右和向下两个位置的最小初始血量影响. 但是这两个位置是我们新开的. 它不应该影响我们要填的这个位置的值.

如果要填的这个红星的位置值为 0 的话, 显然是不对的. 这意味着你从这个地方出发的最低初始血量是 0, 而我们上面说了最低初始血量至少是 1. 而我们想要向右走或者向左走, 进去的最低血量就是 1, 可以理解为救完人后, 出来的最低血量至少是 1. 因此这两个位置的初始化结果为 1
image.png

  1. 填写其他位置的值时

当初始化其他位置的时候,这个时候要填写的位置除了受到新增位置的影响还受到原本矩阵内容的影响. 而我们新增的这些位置不能影响原本的数据. 根据状态转移方程取得是这两个方向上值的最小值. 因此新增的位置的值初始化为无穷大就不会影响我们填写的最终结果了.
image.png

5. 填表顺序

可以看到, 填写 ( i, j ) 位置时, 需要依赖它的后一个和下一个位置的值. 因此我们填表的顺序为 从下到上填写每一行, 而每一行又从右往左填写

6. 返回值

题目要求返回到达 ( m, n ) 位置时的最低初始血量. 而我们的状态表示为从某个位置开始到达终点时所需要的最低初始血量. 也就是 dp[0][0].

7. 代码演示

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {

        // 1. 创建 dp 表
        int m = dungeon.length;
        int n = dungeon[0].length;
        int[][] dp = new int[m + 1][n + 1];

        // 2. 初始化 
        // 将最后一行初始化为无穷大
        for(int j = 0; j <= n; j++) {
            dp[m][j] = Integer.MAX_VALUE;
        }
        // 将最后一列初始化为无穷大
        for(int i = 0; i <= m; i++) {
            dp[i][n] = Integer.MAX_VALUE;
        }
        // 将终点的右边和下边位置初始化为 1
        dp[m][n - 1] = dp[m - 1][n] = 1;

        // 3. 填写 dp 表, 从下往上每一行, 每一行从右往左
        for(int i = m - 1; i >= 0; i--) {
            for(int j = n - 1; j >= 0; j--) {

                // 根据状态转移方程填写
                // 虽然多开了一行一列, 但是对应到原本的 dungeon 的位置没变. 
                // 因为添加的位置是在最后一行和最后一列.
                dp[i][j] = Math.min(dp[i][j + 1], dp[i + 1][j] ) - dungeon[i][j];

                // 如果最终的结果太小为负数, dp 表中表示某个位置为起点的最低初始血量至少大于 1
                // 因此要对这个最终结果进行处理


![img](https://img-blog.csdnimg.cn/img_convert/a7fb2f949eaa48e55cc372cd9d56a501.png)
![img](https://img-blog.csdnimg.cn/img_convert/0945e992105ac11e781d144d4f6f0ccd.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

18636735)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值