Leetcode.174 地下城游戏

题目链接

Leetcode.174 地下城游戏 hard

题目描述

恶魔们抓住了公主并将她关在了地下城 d u n g e o n dungeon dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

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

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

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

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

示例 1:

输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]

输出:7

解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7 。

示例 2:

输入:dungeon = [[0]]

输出:1

提示:

  • m = d u n g e o n . l e n g t h m = dungeon.length m=dungeon.length
  • n = d u n g e o n [ i ] . l e n g t h n = dungeon[i].length n=dungeon[i].length
  • 1 ≤ m , n ≤ 200 1 \leq m, n \leq 200 1≤m,n≤200
  • − 1000 ≤ d u n g e o n [ i ] [ j ] ≤ 1000 -1000 \leq dungeon[i][j] \leq 1000 −1000≤dungeon[i][j]≤1000

解法:动态规划

假设我们考虑从左上角到右下角 ,这样的话我们需要考虑两个因素:当前路径和当前路径上的最小路径和。因为存在两个同等重要的因素,所以我们无法确定下一个位置。

既然从左上角到右下角不行,那么我们就考虑从右下角到左上角。

考虑从右下角到左上角 ,我们定义 f ( i , j ) f(i,j) f(i,j) 为从位置 ( i , j ) (i,j) (i,j) 到终点 ( m − 1 , n − 1 ) (m-1,n-1) (m−1,n−1)所需要的最低初始健康点数。按照定义,最终我们返回的结果就是 f ( 0 , 0 ) f(0,0) f(0,0)。

f ( i , j ) f(i,j) f(i,j) 只与 f ( i + 1 , j ) f(i + 1,j) f(i+1,j) 和 f ( i , j + 1 ) f(i,j+1) f(i,j+1) 以及 d u n g e o n [ i ] [ j ] dungeon[i][j] dungeon[i][j] 有关。

即 f ( i , j ) = m i n { f [ i + 1 ] [ j ] , f [ i ] [ j + 1 ] } − d u n g e o n [ i ] [ j ] f(i,j) = min \{ f[i + 1][j] , f[i][j + 1] \} - dungeon[i][j] f(i,j)=min{f[i+1][j],f[i][j+1]}−dungeon[i][j]。

因为 f [ i ] [ j ] f[i][j] f[i][j] 必须是 ≥ 1 \geq1 ≥1 的,所以最终的转移方程为:

f ( i , j ) = m a x { m i n ( f [ i + 1 ] [ j ] , f [ i ] [ j + 1 ] ) − d u n g e o n [ i ] [ j ] , 1 } f(i,j) = max \{ min ( f[i + 1][j] , f[i][j + 1] ) - dungeon[i][j],1\} f(i,j)=max{min(f[i+1][j],f[i][j+1])−dungeon[i][j],1}

当 i = m − 1 i =m - 1 i=m−1 或者 j = n − 1 j = n- 1 j=n−1时, f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j] 和 f [ i ] [ j + 1 ] f[i][j+1] f[i][j+1] 就会分别越界。初始直接定义 f [ i ] [ j ] f[i][j] f[i][j] 为一个较大的值,这里我设置的是 1 0 9 10^9 109。

特别需要注意的是,我们直接把 f [ m ] [ n − 1 ] f[m][n-1] f[m][n−1] 和 f [ m − 1 ] [ n ] f[m-1][n] f[m−1][n] 设置为 1 1 1,这样是为了让 f [ m − 1 ] [ n − 1 ] = d u n g e o n [ m − 1 ] [ n − 1 ] f[m-1][n-1] = dungeon[m-1][n-1] f[m−1][n−1]=dungeon[m−1][n−1]。

时间复杂度: O ( m × n ) O(m \times n) O(m×n)

C++代码:

cpp 复制代码
class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& g) {
        int m = g.size() , n = g[0].size();
        vector<vector<int>> f(m + 1,vector<int>(n + 1,1e9));

        f[m][n - 1] = 1;
        f[m - 1][n] = 1;

        for(int i = m - 1;i >= 0;i--){
            for(int j = n - 1;j >= 0;j--){
                int t = min(f[i + 1][j],f[i][j + 1]);
                f[i][j] = max(t - g[i][j] , 1);
            }
        }

        return f[0][0];
    }
};
相关推荐
seanli10082 小时前
线性dp 总结&详解
算法·动态规划
Yorelee.6 小时前
保研面试问题总结
深度学习·动态规划
小鱼在乎1 天前
动态规划---最长回文子序列
算法·动态规划
liangbm31 天前
数学建模笔记——动态规划
笔记·python·算法·数学建模·动态规划·背包问题·优化问题
AIAdvocate3 天前
力扣-96.不同的二叉搜索树 题目详解
python·算法·动态规划
GZK.3 天前
【Leetcode】70. 爬楼梯
算法·leetcode·动态规划
源代码•宸4 天前
Leetcode—740. 删除并获得点数【中等】(unordered_map+set+sort)
c++·经验分享·算法·leetcode·动态规划·哈希表
闻缺陷则喜何志丹4 天前
【C++前后缀分解 动态规划】2100. 适合野炊的日子|1702
c++·算法·动态规划·力扣·前后缀分解·日子·适合
jingling5554 天前
后端开发刷题 | 最长上升子序列
java·开发语言·数据结构·后端·算法·动态规划
jingling5554 天前
后端开发刷题 | 打家劫舍
java·开发语言·数据结构·算法·动态规划