[leetcode] 174. Dungeon Game

来源:互联网 发布:电脑设计软件 编辑:程序博客网 时间:2024/05/24 01:33

Question:

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.

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.

-2 (K) -3 3 -5 -10 1 10 30 -5 (P)

Notes:

  • The knight’s health has no upper bound.
  • Any room can contain threats or power-ups, even the first
    room the knight enters and the bottom-right room where the princess is imprisoned.

Solution:

这里想用的是动态规划,从最右下角开始,不断先往左遍历,再往上遍历,求得每一个格子到终点(最右下角)的代价。

每一个格子的代价可以表示为cost[i][j] (第i行,第j列的代价),这个代价只与右方 cost[i][j+1] 或下方 cost[i+1][j] 的格子有关,因为在当前格子只允许往右或下方行走。那么,我们可以只用一个一维数组代替 cost[i][j] ,比如我们用 cost[j] 表示要求的当前格子的代价,在求出这个代价之前,cost[j] 表示的是当前格子的下方格子的代价,而 cost[j+1] 则是刚求出来的右方格子的代价,这样就可以把空间复杂度从O(mn)降低为O(n)

而我们的代价直接使用格子的数值表示,即值越小,则代价越高。当值为0或正数时,生命值只需要1;当为负数时,则需要取反加1,表示需要那么多的生命值,来抵消掉这个代价后,还剩下1(仍然不会死去)。

而这个代价可以这样表示:
1. 初始化时,代价 cost[n]=dungeon[m][n] ,因为终点即起点的话,代价就只是那个格子的数值。
2. 接下来讨论其他情况。假设从当前格子出发可以行走的可能格子(右方或下方,对于边界条件则只为右方或只为下方)中,代价最好的(即cost最大的)为 best ,则分类讨论:

  1. dungeon[i][j] 为负,best 为正,则代价为 dungeon[i][j] ,因为要有足够的生命值才能在这个格子生存下来,再走到 best 所在格子。
  2. dungeon[i][j] 为负,best 为负,则代价为 dungeon[i][j] + best 因为不仅要有足够生命值在当前格子生存下来,还要有足够生命值走到 best
  3. dungeon[i][j] 为正,best 为负,则代价为 dungeon[i][j] + best 因为要有足够的生命值走到 best ,而在走到 best 之前,可以在当前格子获得一定生命值,因此相加。
  4. dungeon[i][j] 为正,best 为正,则代价为 dungeon[i][j] + best ,其实这里加不加都无所谓,因为其实 best 为正时,对结果是没有影响的,只要保证能在当前格子活下来,就能走到 best ,这里相加只是为了便于代码的编写而已。

综上所述,得出来的状态转移方程为:

if (dungeon[i][j] > 0 || cost[j+1] < 0)    cost[j] = dungeon[i][j] + best;else     cost[j] = dungeon[i][j];

详细代码为:
时间复杂度:O(mn)
空间复杂度:O(n)

class Solution {public:    int calculateMinimumHP(vector<vector<int>>& dungeon) {        int m = dungeon.size()-1, n = dungeon[0].size()-1;        int cost[n + 1];        for (int i = m; i >= 0; i--) {            for (int j = n; j >= 0; j--) {                if (i == m && j == n) {                    cost[j] = dungeon[i][j];                } else if (i == m) {                    cost[j] = dungeon[i][j] + ((dungeon[i][j] > 0 || cost[j+1] < 0) ? cost[j+1] : 0);                } else if (j == n) {                    cost[j] = dungeon[i][j] + ((dungeon[i][j] > 0 || cost[j] < 0) ? cost[j] : 0);                } else {                    int best = max(cost[j], cost[j+1]);                    cost[j] = dungeon[i][j] + ((dungeon[i][j] > 0 || best < 0) ? best : 0);                }            }        }        return (cost[0] < 0 ? -cost[0]+1 : 1);    }};