/**
* 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.
*
* Tags:DP, Binary Search
*/
class DungeonGame {
public static void main(String[] args) {
}
/**
* Build a table for the minimum hp needed to get to the bottom right
* Build from bottom right to get minimum from i, j to the end
* Instead of build from to-left, because it's hard to get correct relation
*/
public int calculateMinimumHP(int[][] dungeon) {
if (dungeon == null || dungeon.length == 0 || dungeon[0].length == 0) return 0;
int m = dungeon.length - 1;
int n = dungeon[0].length - 1;
dungeon[m][n] = Math.max(1 - dungeon[m][n], 1);
for (int i = m - 1; i >= 0; i--) dungeon[i][n] = Math.max(dungeon[i + 1][n] - dungeon[i][n], 1);
for (int j = n - 1; j >= 0; j--) dungeon[m][j] = Math.max(dungeon[m][j + 1] - dungeon[m][j], 1);
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
dungeon[i][j] = Math.max(Math.min(dungeon[i + 1][j], dungeon[i][j + 1]) - dungeon[i][j], 1);
}
}
return dungeon[0][0];
}
/**
* Why “from the bottom right corner to left top”?
* It depends on the way you formulate the problem. If you define a value
* in DP table d[i][j] as 'the minimum hp required to REACH (i, j) from (0,
* 0)", then the final answer should be d[nrows-1][ncols-1], and you need
* to start filling from the top left;
* However, in the reference answer provided with the question, dp[i][j] is
* defined as 'the minimum hp required to REACH (nrows-1, ncols-1) from (i,
* j)'. Here dp[0][0] is the final answer so we must fill from (nrows-1,
* ncols-1). For many other problems such as 'Minimum Path Sum', both
* formulation would work.
* However, in this problem, the former formulation will lead us to
* trouble, because it is very hard, if not impossible, to get d[i][j]
* based on d[i-1][j] and d[i][j-1].
*/
}