当前位置:首页 > 牛客 > 牛客网第51817题解题报告:动态规划求解地牢游戏最小血量问题

牛客网第51817题解题报告:动态规划求解地牢游戏最小血量问题

2个月前 (07-22)

牛客网第51817题解题报告:动态规划求解地牢游戏最小血量问题 动态规划 逆向DP 状态转移方程 第1张

一、题目解读

牛客网第51817题要求玩家在地牢游戏中计算从起点到终点所需的最小初始血量。地牢由二维数组表示,每个元素为负数或正数,分别代表扣血或回血。玩家需确保到达终点时血量≥1,求初始最小血量。问题本质是路径规划与资源优化,需考虑路径上的血量动态变化。

二、解题思路

作者采用动态规划(Dynamic Programming)的逆向求解策略:

1. 核心思想:从终点反向推导,计算每个位置所需的最小初始血量。

2. 状态定义:dp[i][j]表示从位置(i,j)到达终点所需的最小初始血量。

3. 状态转移方程:dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]),即从下方或右方相邻位置的最小需求血量减去当前格子扣血值,并确保血量≥1。

4. 边界条件:终点位置dp[n-1][m-1]为max(1, 1 - dungeon[n-1][m-1]),最后一行与最后一列需逆向初始化。

三、解题步骤

1. 输入与初始化:读取地牢矩阵大小n×m,构建dp数组

2. 终点初始化:dp[n-1][m-1]根据终点扣血值计算最小血量。

3. 边界填充:逆向计算最后一行与最后一列的血量需求,确保每个位置血量合法。

4. 动态规划填充:从右下角向左上角遍历,利用状态转移方程更新dp[i][j]。

5. 输出结果:返回dp[0][0]即起点所需最小初始血量。

四、代码与注释

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 计算最小初始血量
int calculateMinimumHP(vector<vector<int>>& dungeon) {
    int n = dungeon.size(); // 地牢行数
    if (n == 0) return 1;   // 边界处理:空地牢返回1
    int m = dungeon[0].size(); // 地牢列数

    // dp[i][j]:从(i,j)到终点所需的最小初始血量
    vector<vector<int>> dp(n, vector<int>(m, 0));
    
    // 1. 终点初始化
    dp[n-1][m-1] = max(1, 1 - dungeon[n-1][m-1]); // 终点血量需≥1,扣血后调整

    // 2. 逆向填充最后一行
    for (int j = m-2; j >= 0; --j) {
        dp[n-1][j] = max(1, dp[n-1][j+1] - dungeon[n-1][j]); // 从右向左推导
    }
    
    // 3. 逆向填充最后一列
    for (int i = n-2; i >= 0; --i) {
        dp[i][m-1] = max(1, dp[i+1][m-1] - dungeon[i][m-1]); // 从下向上推导
    }
    
    // 4. 动态规划填充其余位置
    for (int i = n-2; i >= 0; --i) {
        for (int j = m-2; j >= 0; --j) {
            int min_required = min(dp[i+1][j], dp[i][j+1]); // 取下方/右方较小需求
            dp[i][j] = max(1, min_required - dungeon[i][j]); // 扣当前格子血量并确保≥1
        }
    }
    
    return dp[0][0]; // 返回起点所需最小血量
}

int main() {
    ios::sync_with_stdio(false); // 加快输入输出
    cin.tie(nullptr);
    
    int n, m; // 输入地牢尺寸
    cin >> n >> m;
    vector<vector<int>> dungeon(n, vector<int>(m)); // 构建地牢矩阵
    
    // 输入地牢元素
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> dungeon[i][j];
        }
    }
    
    cout << calculateMinimumHP(dungeon) << endl; // 输出结果
    return 0;
}

五、总结

本文通过动态规划逆向求解地牢游戏的最小血量问题,核心在于合理定义状态与推导转移方程。代码通过优化边界处理与血量约束,高效计算出全局最优解。该解法适用于存在“终点约束”的路径规划问题,体现动态规划在资源优化中的灵活应用。掌握此类思路,有助于提升算法设计与面试解题能力。

参考:牛客51817题解

原创内容 转载请注明出处

分享给朋友:

相关文章

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

题目重解:小A带着m元钱来到餐馆,菜单上有n道菜,每道菜都有确定的价格。现在需要计算出刚好花完m元的点菜方案总数。这个问题看似简单,但当菜品数量增多时,暴力枚举就会变得不可行,需要更高效的算法来解决。...

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

一、题目解读2019年CSP-J的“纪念品”问题(对应洛谷P5662)要求玩家在T天内通过买卖纪念品最大化金币收益。每天可交易N种商品,需计算最优策略下的最终金币数。题目强调动态规划思维与资源分配优化...

CSP-J方格取数题解|动态规划解法|洛谷P7074代码解析

CSP-J方格取数题解|动态规划解法|洛谷P7074代码解析

一、题目解读题目要求在一个n×m的网格中,从左上角到右下角选择一条路径,路径上的数字可重复取用,求取数之和的最大值。路径限制为仅能向右或向下移动。需注意路径的灵活性与重复取数的可能性,传统单向动态规划...

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

一、题目解读牛客12576题是一道经典的算法题,要求给定起点N和终点M,求解从N到M的最少跳跃次数。题目考察的核心在于路径优化与动态规划思想,需结合数论中的质因数分解技巧,通过合理设计算法降低时间复杂...

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

1999年NOIP提高组导弹拦截(洛谷P1020)解题思路与动态规划代码解析

1999年NOIP提高组导弹拦截(洛谷P1020)解题思路与动态规划代码解析

一、题目解读    1999年NOIP提高组“导弹拦截”问题(对应洛谷P1020)要求设计导弹拦截系统:给定一组导弹高度数据,需计算最少拦截系统数量,并求最多能...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。