Dungeon Game

Solve this Problem
Hard25–30 min
Topics
Companies
Practice:LeetCode ↗
Given a grid where every cell either subtracts from or adds to a running value, and starting at the top-left corner with only right and down moves allowed, find the smallest possible starting value that keeps the running total at 1 or above at every single cell along some path to the bottom-right corner — a total that ever drops to 0 or below along the way makes that path invalid, no matter how it recovers afterward. Working backward from the bottom-right corner makes this tractable: the minimum value needed to safely enter any cell depends only on the minimum value needed to enter whichever of the two cells after it — one step down, one step right — turns out to be the safer choice, adjusted for this cell's own effect and never allowed to drop below 1. Sweeping backward from the bottom-right corner to the top-left, one row at a time, resolves every cell using requirements already computed for the cells that follow it, and the top-left cell's requirement is the answer.

Test Case 1:

Input:dungeon = [[-3,5,2],[1,-4,1],[2,-1,3]]
Output:4
Explanation:Starting with 4 and taking the path -3→1→2→-1→3 (down, down, right, right), the running total never drops to 0 or below at any cell — the minimum possible starting value.

Test Case 2:

Input:dungeon = [[3,-2],[-4,1]]
Output:1
Explanation:The path 3→-2→1 leaves the running total at 1 then 2 after the first cell, so a starting value of 1 is already enough.

Test Case 3:

Input:dungeon = [[6]]
Output:1
Explanation:A single cell with a positive value needs no cushion — 1 is always the minimum starting value, even though the cell would add more.

Constraints

  • 1 ≤ dungeon.length, dungeon[0].length ≤ 15
  • -1000 ≤ dungeon[i][j] ≤ 1000
🚀

Try the Dry Run

Don't just read the solution — watch it execute, one step at a time.

Approach & Solutions

Brute Force — Recursive Without Memoization

Brute

The running total must stay at least 1 at every cell it passes through, so work out the minimum value needed entering each cell to keep it that way all the way to the bottom-right corner. At the bottom-right cell itself, the minimum entering value is whatever keeps the total at least 1 after this cell's own effect, which is max(1, 1 - cell's value). At any other cell, the path continues to whichever of the two next cells — one step down or one step right — needs less entering value to survive from there, so the value needed entering the current cell is that smaller requirement minus this cell's own effect, floored at 1 (since the running total can never be recorded as 0 or negative). Stepping past the bottom or right edge simply isn't a valid move. Recursing from the top-left corner finds the minimum starting value, though the same cell ends up recomputed every time a different path reaches it.

TimeO(2^(m+n))
SpaceO(m + n)
1class Solution { 2 private int[][] dungeon; 3 private int m; 4 private int n; 5 6 public int calculateMinimumHP(int[][] dungeon) { 7 this.dungeon = dungeon; 8 this.m = dungeon.length; 9 this.n = dungeon[0].length; 10 return solve(0, 0); 11 } 12 13 private int solve(int i, int j) { 14 if (i == m - 1 && j == n - 1) { 15 return Math.max(1, 1 - dungeon[i][j]); 16 } 17 int down = (i + 1 < m) ? solve(i + 1, j) : Integer.MAX_VALUE; 18 int right = (j + 1 < n) ? solve(i, j + 1) : Integer.MAX_VALUE; 19 int need = Math.min(down, right) - dungeon[i][j]; 20 return Math.max(1, need); 21 } 22}

Optimal — Bottom-Up 1D DP

Optimal

Work backward from the bottom-right corner. Track, for the row currently being processed, the minimum value needed entering each column to keep the running total at least 1 all the way to the end. Two sentinel facts kick off the sweep: finishing at the bottom-right cell needs no further cushion beyond surviving that cell itself, and there's no valid move past the last column. Then, moving from the bottom row upward and from the last column backward within each row, every cell's requirement is the smaller of the two requirements below/to the right of it, minus this cell's own effect, floored at 1 so a positive-value cell never produces a requirement below 1. Once every row has been swept through this way, the first column of the top row holds the minimum starting value for the whole grid.

TimeO(m × n)
SpaceO(n)
1class Solution { 2 public int calculateMinimumHP(int[][] dungeon) { 3 int m = dungeon.length, n = dungeon[0].length; 4 int[] dp = new int[n + 1]; 5 Arrays.fill(dp, Integer.MAX_VALUE); 6 dp[n - 1] = 1; 7 for (int i = m - 1; i >= 0; i--) { 8 dp[n] = (i == m - 1) ? 1 : Integer.MAX_VALUE; 9 for (int j = n - 1; j >= 0; j--) { 10 int need = Math.min(dp[j], dp[j + 1]) - dungeon[i][j]; 11 dp[j] = Math.max(1, need); 12 } 13 } 14 return dp[0]; 15 } 16}

Related Problems