【LeetCode 174】Dungeon Game
The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon.
The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned
in the top-left room and must fight his way through the dungeon to rescue the princess.
The knight has an initial health point represented by a positive integer. If at any point his health point
drops to 0 or below, he dies immediately.
Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering
these rooms; other rooms are either empty (0's) or contain magic orbs that increase the knight's health
(positive integers).
In order to reach the princess as quickly as possible, the knight decides to move only rightward or
downward in each step.
Write a function to determine the knight's minimum initial health so that he is able to rescue the
princess.
题意:给出一个M*N的迷宫,玩家从左上角K开始寻找路径走到右下角P,只能向右或向下走。
玩家有个初始血量,经过一格时血量会与格子中的数字相加(负数也就意味着血量减少)。在游戏
的任意时刻,只要玩家血量小于或等于0则失败。求玩家要想成功走到右下角所需要的最少初始
血量。
For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN
.
思路:根据题目类型,应该马上想到动态规划。类似的题目有很多,比如寻找从左上角到右下角的最小代价路径、路径总数量等,但本题与这些题有略微的不同。在计算最小代价路径时,从左上到右下计算dp矩阵即可。但本题得从右下逆向往左上计算。
假如从左上往右下dp,每一格 dp[i][j]=min(dp[i-1][j],dp[i][j-1]) 代表到达这一格的最少血量消耗,但这样只能计算出最后到达右下角的最小血量消耗,忽略了一个条件:到任何一格时玩家的血量都必须是正数才行。如果本题改成血量可以降到0及0以下,求到达右下角的最多剩余血量,那么就是传统的最小代价路径求解问题。但血量为正的条件使得这个最小代价路径的途中玩家可能会因血量太低而死亡。
所以改成从右下往左上dp,每一格 dp[i][j] 代表踏入这一格需要的最少血量。在走完最后一个房间的时候血量至少要剩下1,因此最后的状态可以当成是初始状态,由后往前依次决定在每一个位置至少要有多少血量, 这样一个位置的状态是由其下面一个和和左边一个的较小状态决定的。
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int ROW = dungeon.size(), COL = dungeon[0].size();
//多一列只是为了放初始值1
vector<vector<int>> dp(ROW, vector<int>(COL + 1));
dp[ROW - 1][COL] = 1;
//初始化最下行
for (int c = COL - 1; c >= 0; c--) {
dp[ROW - 1][c] = max(dp[ROW - 1][c + 1] - dungeon[ROW - 1][c], 1);
}
//初始化最右列
for (int r = ROW - 2; r >= 0; r--) {
dp[r][COL - 1] = max(dp[r + 1][COL - 1] - dungeon[r][COL - 1], 1);
}
//dp递推式
for (int r = ROW - 2; r >= 0; r--) {
for (int c = COL - 2; c >= 0; c--) {
int curHealth = min(dp[r][c + 1], dp[r + 1][c]);
dp[r][c] = max(curHealth - dungeon[r][c], 1);
}
}
return dp[0][0];
}