174-地下城游戏-初遇有后效性问题的DP

题目

思路分析

看到这道DP问题,第一反应就是跟 62.不同路径63.不同路径Ⅱ 特像,然后根据之前的思路,直接声明一个dp数组,然后从头到尾更新dp数组取最低的初始健康点数似乎可行,但是因为在地图中既有扣血的恶魔也有加血的魔法球,因此要对加血后的血量状态值进行保存,所以还需要一个额外的helper数组来存储骑士当前的健康状态(即走过的路的路径和)。
然后就要考虑怎么进行状态转移了,如果片面的考虑题目给出的例子,最后期望的是得到最小的dp数组的值,所以优先考虑dp数组的值决定走哪一格,然后dp数组相等的情况再来判断helper数组的值似乎是合理的。于是就会得到如下的状态转移方程:

    if (dp[i - 1][j] > dp[i][j - 1]) {
        helper[i][j] = helper[i][j - 1] - dungeon[i][j];
        dp[i][j] = Math.max(dp[i][j - 1], helper[i][j]);
    } else if (dp[i - 1][j] == dp[i][j - 1]) {
        helper[i][j] = Math.min(helper[i - 1][j], helper[i][j - 1]);
        dp[i][j] = Math.max(helper[i][j],
        (helper[i - 1][j] > helper[i][j - 1] ? dp[i][j - 1] : dp[i - 1][j]));
    } else {
        helper[i][j] = helper[i - 1][j] - dungeon[i][j];
        dp[i][j] = Math.max(helper[i][j], dp[i - 1][j]);
    }

但是,这样考虑其实是不正确的,因为官方给出的例子毕竟还是片面的,我们可以考虑如下的例子:


当计算到图中第一个问好位置时,dp[1][1] < dp[0][2],而helper[1][1] > helper[0][2],这时候如果按照上边的算法来选择路径会选择图中的绿色路径,得到答案 4,不过这样并不是最优解,应当走红色路径,得到答案 2。这是因为最后一个格子为 -3,如果最后一个格子为0,那么又应该走图中的绿色路线。
通过这里可以发现上边的选路方法并不正确,另外,通过官方例子和上边的例子,dp[i][j]的大小和helper[i][j]的大小似乎都不能进行路径的选择,综合到一起也不能得出正确答案,因为具体的走那条路还和后边的格子有关,需要根据后边的格子进行选择,所以这样判断必然要考虑所有方案,也就是相当于进行递归遍历了,显然这种想法不能解决问题。
这道题我做到这里就想不出什么思路了,于是参考了题解,才得知像这样两个同等重要的参数同时影响后续决策的问题,不满足动态规划的[无后效性],所以这样遍历是得不到绝对正确的答案的。正确的遍历方式应当是倒序遍历,从右下至左上进行遍历,而为什么这么遍历呢?评论区的Shine Hugh大佬给的解释十分清晰:

确实如他所说,直接使用不同问题的思路进行左上开始遍历并不是基于已知解的,在划分子问题的时候就已经错了,如果子问题划分为其所说的“从i, j到达终点需要的最小生命值”,这样很容易想到初始状态为从终点到终点需要的最小生命值,那么从右下到左上遍历也就显而易见了。

完整代码

class Solution {
    public int calculateMinimumHP(int[][] dungeon) {
        if (dungeon == null)
            return 0;
        int n = dungeon.length;
        if (n == 0)
            return 0;
        int m = dungeon[0].length;
        int[][] dp = new int[n + 1][m + 1];// dp[i][j]表示走到i , j 格所需要的最低初始健康点数
        for(int i = 0; i <= n; i++){
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        dp[n - 1][m] = dp[n][m - 1] = 1;

        for(int i = n - 1; i >= 0; i--){
            for(int j = m - 1; j >= 0; j--){
                dp[i][j] = Math.max(Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j], 1);
            }
        }
        return dp[0][0];
    }
}

这里还要注意出现某一个格子吃到魔法球使得所需生命值变为0,或者负数的情况,虽然变成负数,但是负数只对该格子后边的路径有影响,对前边没遍历到的格子没有影响,所以保证最低血量为1即可。

总结

之前也做了不少的DP问题了,但是做的都是正向遍历和反向遍历都可以的,还是第一次碰到这种考虑到后效性的问题,特此记录一下,方便以后复习查看。如果有写的不正确的地方还请指出。感恩相遇~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356