Python 0174.py
class Solution:
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
width = len(dungeon[0])
height = len(dungeon)
hp = []
for y in range(height):
hp.append([ 999999999 ] * width)
# travel in reverse, need 1 HP plus whatever the last room
hp[height-1][width-1] = 1 - min(0, dungeon[height-1][width-1])
todo = []
if width > 1 or height > 1:
todo = [ [ height-1, width-1] ]
while todo:
y, x = todo.pop()
health = hp[y][x]
if x > 0:
need = max(1, health - dungeon[y][x-1])
if hp[y][x-1] > need:
hp[y][x-1] = need
todo.append([ y, x-1 ])
if y > 0:
need = max(1, health - dungeon[y-1][x])
if hp[y-1][x] > need:
hp[y-1][x] = need
todo.append([ y-1, x ])
return hp[0][0]
Difficulty
and has an acceptance rate of about 42% (700k+ submissions).
Typical patterns to solve this problem:
- Array
- Dynamic Programming
- Matrix
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation computes the minimum initial health required for a knight to survive a grid-based dungeon, moving only rightward or downward. It works backwards from the destination cell, propagating the health requirement to predecessor cells until the start cell is resolved. The algorithm stores intermediate results in a two-dimensional array and uses an explicit stack to drive the propagation.
Key Steps
Dimension Extraction and Matrix Allocation
width = len(dungeon[0])
height = len(dungeon)
hp = []
for y in range(height):
hp.append([ 999999999 ] * width)
The code first determines the grid dimensions and creates a matrix hp of the same size, filled with a large sentinel value. This sentinel represents an unknown or infinitely large health requirement before any computation.
Base Case Initialization
hp[height-1][width-1] = 1 - min(0, dungeon[height-1][width-1])
The destination cell is initialized to the minimum health needed to survive that cell. If the cell contains a non-positive value, the knight must have at least 1 - cell_value health; otherwise a single health point suffices.
Stack Preparation
todo = []
if width > 1 or height > 1:
todo = [ [ height-1, width-1] ]
When the grid contains more than one cell, the bottom-right coordinate is placed on a stack named todo. This stack drives the reverse traversal.
Reverse Propagation Loop
while todo:
y, x = todo.pop()
health = hp[y][x]
if x > 0:
need = max(1, health - dungeon[y][x-1])
if hp[y][x-1] > need:
hp[y][x-1] = need
todo.append([ y, x-1 ])
if y > 0:
need = max(1, health - dungeon[y-1][x])
if hp[y-1][x] > need:
hp[y-1][x] = need
todo.append([ y-1, x ])
Inside the loop the algorithm extracts a cell from the stack, reads its current health requirement, and examines its left and upper neighbours (the only cells that can move into the current one). For each neighbour it computes the health needed to step into the current cell, ensuring the result never drops below one. If this computed requirement improves the neighbour’s stored value, the neighbour is updated and pushed onto the stack for further propagation.
Result Retrieval
return hp[0][0]
After the stack is exhausted, the top-left entry of hp holds the minimum initial health needed at the start of the dungeon.
Edge Cases Handled
When the dungeon consists of a single cell, the stack is left empty because no propagation is required. The base case assignment directly yields the correct answer for that scenario.
If a cell contains a large positive number, the max(1, ...) guard ensures that the health requirement never falls below one, preventing an underflow to zero or negative health.
When a cell contains a large negative number, the subtraction health - dungeon[...] correctly increases the required health, and the sentinel value 999999999 guarantees that the first computed need will replace it.
Zero-valued cells are treated like neutral rooms; the max(1, ...) clause still enforces a minimum of one health point, satisfying the rule that the knight must stay alive after each move.
Performance Optimizations
The algorithm avoids recursion by using an explicit stack, eliminating the overhead of function call management and enabling in-place updates of the hp matrix.
The sentinel value initialization prevents repeated boundary checks inside the loop; each cell’s health requirement is only updated when a strictly better (lower) value is found, reducing unnecessary stack pushes.
The use of max(1, ...) consolidates the lower-bound enforcement into a single arithmetic operation, avoiding separate conditional branches for positive and negative adjustments.
By updating neighbours only when the new requirement is smaller than the existing entry, the code minimizes the number of times a cell is revisited, leading to fewer iterations of the main loop.
Overall, the design leverages a forward-compatible data structure (hp) and a depth-first style stack to propagate constraints efficiently across the grid.
ELI5 - Simplified Explanation
Imagine a big castle made of many square rooms laid out like a floor made of tiles. Inside some of these rooms are magic treasure chests that give a knight extra energy, and in other rooms are mean dragons that take energy away. The goal is for the knight to walk from the very last room back to the very first room while always staying strong enough to survive the dragons.
The computer starts at the very end of the castle and figures out exactly how much energy is needed at the last tile to make it through safely. It then looks at the tiles right before that one. It thinks about whether the knight would need more or less energy to pass through the dragon in the previous room.
A list called todo helps the computer keep track of which rooms it still needs to check. It picks a room from this list and looks at the neighbors to the left and above it. It calculates the minimum amount of energy required to face whatever is in those neighboring rooms. If the computer finds a smaller number than it saw before, it remembers that new amount and adds that room to the list so it can look at the ones before that too.
By working backward from the end to the start, the computer builds a map of the exact energy needed at every step of the journey. Once the computer works its way all the way back to the very first room at the beginning of the castle, it knows exactly how much energy is needed to start the adventure so that the knight can get through the whole castle without ever running out.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||||
| www | AlgoMonster | C++ | Python | Java | TS | |||||
| WalkCCC | C++ | Python | Java | |||||||
| leetcode.ca | C++ | Python | Java | C# | Go | |||||
| LeetcodeHelp | C++ | Python | Java | |||||||
| MemoryLimitExceeded | C++ | Python | ||||||||
| Progiez | C++ | Python | Java | |||||||
| github | aylei | Rust | ||||||||
| begeekmyfriend | C | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | Python | |||||||||
| charles-wangkai | Java | |||||||||
| chenyanzhe | C++ | |||||||||
| everthis | JS | |||||||||
| fpdjsns | C++ | |||||||||
| gaosq0604 | Python | |||||||||
| gouthampradhan | Java | |||||||||
| halfrost | Go | |||||||||
| haoel | C++ | |||||||||
| JoshCrozier | JS | |||||||||
| keineahnung2345 | C++ | |||||||||
| linouk23 | C++ | |||||||||
| lkwq007 | Python | |||||||||
| MacJim | C++ | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| wisdompeak | C++ | |||||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||||
| WalkCCC | C++, Python, Java | |||||||||
| leetcode.ca | C++, C#, Go, Java, Python | |||||||||
| LeetcodeHelp | C++, Java, Python | |||||||||
| MemoryLimitExceeded | Python, C++ | |||||||||
| Progiez | C++, Java, Python | |||||||||
| aylei | Rust | |||||||||
| begeekmyfriend | C | |||||||||
| BigEggStudy | C# | |||||||||
| black-shadows | Python, Python | |||||||||
| charles-wangkai | Java | |||||||||
| chenyanzhe | C++ | |||||||||
| everthis | JavaScript | |||||||||
| fpdjsns | C++ | |||||||||
| gaosq0604 | Python | |||||||||
| gouthampradhan | Java | |||||||||
| halfrost | Go | |||||||||
| haoel | C++ | |||||||||
| JoshCrozier | JavaScript | |||||||||
| keineahnung2345 | C++ | |||||||||
| linouk23 | C++ | |||||||||
| lkwq007 | Python | |||||||||
| MacJim | C++ | |||||||||
| ruoru | Rust | |||||||||
| suxrobGM | C# | |||||||||
| wisdompeak | C++ |
Videos
Similar Problems
- problem 64: Minimum Path Sum
- problem 741: Cherry Pickup
Solved On The Same Day
- problem 175: Combine Two Tables
- problem 181: Employees Earning More Than Their Managers
- problem 182: Duplicate Emails
- problem 183: Customers Who Never Order
- problem 193: Valid Phone Numbers
- problem 196: Delete Duplicate Emails
- problem 197: Rising Temperature
- problem 203: Remove Linked List Elements
- problem 511: Game Play Analysis I
- problem 577: Employee Bonus
- problem 584: Find Customer Referee
- problem 586: Customer Placing the Largest Number of Orders
- problem 595: Big Countries
- problem 596: Classes With at Least 5 Students
- problem 607: Sales Person
- problem 610: Triangle Judgement
- problem 619: Biggest Single Number
- problem 620: Not Boring Movies
- problem 627: Swap Sex of Employees
- problem 1050: Actors and Directors Who Cooperated At Least Three Times
- problem 1068: Product Sales Analysis I
- problem 1075: Project Employees I
- problem 1084: Sales Analysis III
- problem 1141: User Activity for the Past 30 Days I
- problem 1148: Article Views I
- problem 1179: Reformat Department Table
- problem 176: Second Highest Salary
- problem 177: Nth Highest Salary
- problem 178: Rank Scores
- problem 180: Consecutive Numbers
- problem 184: Department Highest Salary
- problem 192: Word Frequency
- problem 194: Transpose File
- problem 550: Game Play Analysis IV
- problem 570: Managers with at Least 5 Direct Reports
- problem 585: Investments in 2016
- problem 602: Friend Requests II: Who Has the Most Friends
- problem 608: Tree Node
- problem 626: Exchange Seats
- problem 725: Split Linked List in Parts
- problem 1045: Customers Who Bought All Products
- problem 1070: Product Sales Analysis III
- problem 1164: Product Price at a Given Date
- problem 1174: Immediate Food Delivery II
- problem 1193: Monthly Transactions I
- problem 1204: Last Person to Fit in the Bus
- problem 1321: Restaurant Growth
- problem 1393: Capital Gain/Loss
- problem 185: Department Top Three Salaries
- problem 218: The Skyline Problem
- problem 262: Trips and Users
- problem 601: Human Traffic of Stadium
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality