地下城游戏(回头看)

dp问题描述

地下城游戏
在这里插入图片描述
请你看上面示例一中的标准答案那条路线,以及下下右右这条路线。非常显然,当骑士走到最后的时候,第二条路线的剩余体力是最多的。但是实际上第一条路线和第二条路线对于骑士来说是等价的,因为在这两条路线行走的过程中,骑士的体力相对于他进迷宫之前最多下降7点体力,因此我们要求的并不是骑士走完这条路线的体力剩余,而是骑士在这条路线中体力最多降低多少。

确定本题的状态表示

本题倒着做是比较方便的,我一开始正着做感觉还是很折磨的(因为这样做是有后效性的,一个节点的取值不仅受到左边和上边的格子影响,还可能受到右边和下边的格子的影响),最后47个测试用例过了46个,最后一个真没办法,因为如果把最后一个改对了,我一运行,就只能过36个了。
言归正传,在本题中我们规定:dp [i][j] 表示从 [i, j] 位置出发,到达终点,所需的最低初始健康点数

确定本题的状态转移方程

在这里插入图片描述
在理解这个运行逻辑的时候,建议举一个最简单的例子。
vector<vector<int>>& dungeon={{0,-5}}
在这个例子中,我们要想让骑士活着救出公主,那么他的体力值在进入地下城之前应该是6点,初始情况下我们会令dp[1][1]=1,那么要计算dp[1][2]就应该拿1-(-5)=6,他表示的含义就是我想要在走出迷宫的时候,体力值大于等于一,那我在进入最后一格之前,我的体力值就应该大于等于 6。

填表求值

根据初始条件和状态转移方程,确定填表顺序,进而逐步填满dp表,最终返回题目要的结果

代码实现

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& d) {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值
        int m = d.size(), n = d[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MAX));
        dp[m][n - 1] = dp[m - 1][n] = 1;
        for(int i = m - 1; i >= 0; i--)
            for(int j = n - 1; j >= 0; j--)
            {
                dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) - d[i][j];
                dp[i][j] = max(1, dp[i][j]);
            }
        return dp[0][0];
    }
};

下面附上我一个小时44/45的丑陋代码,虽然拼尽全力无法战胜,但我觉得应该给我那逝去的一个小时做一个纪念

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m=dungeon.size();
        if(m==0) return 0;
        int n=dungeon[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        vector<vector<int>> min_differ(m+1,vector<int>(n+1,0));
        cout << m << n<< endl;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                cout << "i="<<i<<" j=" << j<< endl;
                if(i==1){
                    dp[i][j]=dp[i][j-1]+dungeon[i-1][j-1];
                    min_differ[i][j]=min(min_differ[i][j-1],dp[i][j]);
                    
                } 
                else if(j==1) {
                    dp[i][j]=dp[i-1][j]+dungeon[i-1][j-1];
                    min_differ[i][j]=min(min_differ[i-1][j],dp[i][j]);
                }
                else {
                    dp[i][j]=max(dp[i][j-1],dp[i-1][j])+dungeon[i-1][j-1];
                    int left_dp=dp[i][j-1]+dungeon[i-1][j-1];
                    int right_dp=dp[i-1][j]+dungeon[i-1][j-1];
                    int left=min(min_differ[i][j-1],left_dp);
                    int up=min(min_differ[i-1][j],right_dp);
                    min_differ[i][j]=max(left,up);
                }
                cout << "dp["<<i<<"]["<<j<<"]="<<dp[i][j];
                cout << " min_differ["<<i<<"]["<<j<<"]="<<min_differ[i][j]<< endl;
            }
        }
        return 1-min_differ[m][n];
    }
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值