LeetCode 174: Dungeon Game 解题与思考
题目描述
给一个M×N的地图,每个格子有一个数字,负数为扣减生命值,正数为增加生命值,当生命值小于等于0时角色死亡,求解从地图左上角到右下角所需的最小生命值。
思路
题目限定了骑士只能往右或者下走,给我们提供了很大方便。
不过直接从左上DP到右下不是很理智,因为没有确定初始生命值,不能很好地把握角色死亡的标准。
然而在到达右下角时,为了解最优,角色一定剩下1血,故应该从右下角DP回左上角。
我们用一个二维数组HP[m][n]记录从某一格到右下角需要的生命值,假设当前地图的数字为dungeon[i][j],那么有如下递推式:
HP[i][j]+dungeon[i][j]=min(HP[i][j+1],HP[i+1][j])
即
HP[i][j]=min(HP[i][j+1],HP[i+1][j])−dungeon[i][j]
也就是说,当前格子结算完毕后,我们朝着所需生命值更少的方向前进。
不过我们需要考虑dungeon[i][j]十分大的情况,也就是说只要能成功到达这格就能到达目标地点,此时HP[i][j]直接为1。
算法
1、为了计算方便,初始化数组HP[M+1][N+1],HP[M][0~N-2] =HP[0~M - 2][N] = 2147483647,让DP不会跑到地图外;HP[M][N-1] = HP[M-1][N] = 1,让右下角正确剩余1血。
2、从右下角到左上角,按照如下方法更新:
HP_need=min(HP[i][j+1],HP[i+1][j])−dungeon[i][j]
- 假如 HP_need<=0 ,那么 HP[i][j]=1 ;
- 假如 HP_need>0 ,那么 HP[i][j]=HP_need ;
3、最后返回 HP[0][0]
代码
#include <iostream>
#include <vector>
#include <stdlib.h>
#include<string.h>
using namespace std;
class Solution {
int min(int x, int y) {
return (x < y) ? x : y;
}
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int M = dungeon.size();
int N = dungeon[0].size();
int *health_need = (int*)malloc((M + 1) * (N + 1) * sizeof(int));
memset(health_need, 0, (M + 1) * (N + 1) * sizeof(int));
for ( int m = M - 2; m >= 0; m-- ) {
health_need[m * (N + 1) + N] = 2147483647;
}
for ( int n = N - 2; n >= 0; n-- ) {
health_need[M * (N + 1) + n] = 2147483647;
}
health_need[M * (N + 1) + N - 1] = 1;
health_need[(M - 1) * (N + 1) + N] = 1;
for ( int m = M - 1; m >= 0; m-- ) {
for ( int n = N - 1; n >= 0; n-- ) {
int current_health = min(health_need[m * (N + 1) + n + 1], health_need[(m + 1) * (N + 1) + n]) - dungeon[m][n];
health_need[m*(N + 1) + n] = (current_health <= 0) ? 1 : current_health;
}
}
int result = health_need[0];
free(health_need);
return result;
}
};
思考
一开始并没有考虑补右边和下边那一条大边,结果写起来很麻烦,最后才想到这么做,实属惭愧。