The demons had captured the princess 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 was initially positioned in the top-left room and must fight his way through 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 (represented by negative integers), so the knight loses health upon entering these rooms; other rooms are either empty (represented as 0) or contain magic orbs that increase the knight's health (represented by positive integers).
To reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step.
Return the knight's minimum initial health so that he can rescue the princess.
Note that 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.
Example 1:
Input: dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]] Output: 7 Explanation: The initial health of the knight must be at least 7 if he follows the optimal path: RIGHT-> RIGHT -> DOWN -> DOWN.
Example 2:
Input: dungeon = [[0]] Output: 1
Constraints:
m == dungeon.length
n == dungeon[i].length
1 <= m, n <= 200
-1000 <= dungeon[i][j] <= 1000
This problem asks for the minimum initial health required for a knight to traverse a dungeon from the top-left to the bottom-right, collecting health boosts and enduring health reductions along the way. The knight can only move right or down. The solution uses dynamic programming.
Approach:
We use a dynamic programming approach to solve this problem efficiently. The key idea is to build a DP table where dp[i][j]
represents the minimum health the knight needs at cell (i, j)
to guarantee survival to the end. We work backwards from the princess's location (bottom-right) to the starting location (top-left).
DP Relation:
To reach a cell (i, j)
, the knight must have come from either cell (i+1, j)
(down) or (i, j+1)
(right). The minimum health needed at (i, j)
is determined by the minimum of the health needed at these two neighboring cells, considering the health change at (i, j)
:
minHealthNeededAt(i, j) = min(healthNeededAt(i+1, j), healthNeededAt(i, j+1))
However, we need to account for the fact that the health at cell (i, j)
can either increase or decrease. We need to ensure the knight doesn't have health less than or equal to 0 at any point. This leads to the final DP relation:
dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
The max(1, ...)
ensures the knight always has at least 1 health point.
Base Cases:
1
(to survive entering that final cell, regardless of the value in the cell).Algorithm:
Initialization: Create a DP table dp
with dimensions one larger than the dungeon in both directions. Initialize all cells to a large value (e.g., infinity) except for the base cases (bottom-right and the cells on the rightmost and bottommost columns), which are set to 1.
Iteration: Iterate through the DP table from the bottom-right to the top-left, using the DP relation to calculate the minimum health needed at each cell.
Result: The minimum initial health required is stored in dp[0][0]
.
Time and Space Complexity:
Code Examples (with explanations incorporated into comments):
(Python)
import sys
def calculateMinimumHP(dungeon):
m, n = len(dungeon), len(dungeon[0])
# DP table: Initialize with a large value (infinity)
dp = [[sys.maxsize] * (n + 1) for _ in range(m + 1)]
# Base cases: Bottom-right and last rows/columns need at least 1 health
dp[m][n - 1] = dp[m - 1][n] = 1
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
# DP relation: Minimum health needed = max(1, min(health from below, health from right) - cell value)
dp[i][j] = max(1, min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j])
# Result is at the top-left cell
return dp[0][0]
Other languages (Java, C++, Go, C#) follow similar logic with appropriate syntax changes. The comments in the Python code above highlight the key steps of the algorithm and explain the DP relation. The other language versions are structurally very similar.