Problem
Given a triangle (a nested array where row i contains exactly i + 1 numbers), find the minimum sum path from the top element down to the bottom row. At each step you may move to the same index or the next index in the row below โ there is no moving left or diagonally backward.
- Input:
[[2],[3,4],[6,5,7],[4,1,8,3]] - Output:
11 - Explanation: The path 2 โ 3 โ 5 โ 1 sums to 11, which is the smallest possible.
A greedy counter-example: the triangle [[-1],[2,3],[1,-1,-3]] has minimum path โ1 โ 3 โ โ3 = โ1, not โ1 โ 2 โ โ1 = 0 โ showing that locally smaller neighbors don't always win.
Intuition
Every root-to-bottom path touches exactly one cell per row, and the only choices at each step are "straight down" or "diagonal right." The best total cost from any cell depends only on the best costs of the two cells reachable below it โ a clean recursive sub-structure. Building the answer from the bottom eliminates the need to track paths explicitly.
Approach 1 โ Top-Down with Memoization
Start at the apex and recurse downward, caching each (row, col) result so it is computed at most once.
- Define
min_path(row, col)as the minimum sum achievable starting at that cell. - Base case: the last row has no children, so return the cell value directly.
- Recurse to the two reachable children โ same column and column + 1 in the next row.
- Cache the result before returning so overlapping sub-problems are answered in O(1).
- The answer is
min_path(0, 0).
1from functools import lru_cache
2
3def minimumTotal(triangle):
4 n = len(triangle)
5
6 @lru_cache(maxsize=None)
7 def min_path(row, col):
8 if row == n - 1:
9 return triangle[row][col]
10 # Only two legal moves: same col or col+1 in the next row
11 return triangle[row][col] + min(
12 min_path(row + 1, col),
13 min_path(row + 1, col + 1)
14 )
15
16 return min_path(0, 0)Time: O(nยฒ) โ each of the n(n+1)/2 cells is visited and stored exactly once.
Space: O(nยฒ) โ the memo table holds one entry per cell plus O(n) recursion stack depth.
Approach 2 โ Bottom-Up DP (Space-Optimized)
Copy the last row into a 1D array and propagate minimum costs upward row by row, reusing the same array.
- Initialize
dpas a copy of the bottom row โ these are the trivial single-cell path sums. - For each row from
n - 2down to0, iterate columns0throughrow(inclusive). - Overwrite
dp[col]withtriangle[row][col] + min(dp[col], dp[col + 1]). - Left-to-right iteration is safe:
dp[col + 1]still holds the previous row's value whendp[col]is computed. - After all rows,
dp[0]is the answer.
1def minimumTotal(triangle):
2 dp = triangle[-1][:] # copy last row; these are the base-case path costs
3
4 for row in range(len(triangle) - 2, -1, -1):
5 for col in range(len(triangle[row])):
6 # dp[col+1] hasn't been updated yet this iteration, so it's safe to read
7 dp[col] = triangle[row][col] + min(dp[col], dp[col + 1])
8
9 return dp[0]Time: O(nยฒ) โ every cell is processed once.
Space: O(n) โ a single array of size n replaces the full 2D memo table.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Top-Down Memoization | O(nยฒ) | O(nยฒ) | Debugging or learning โ the recursion mirrors the problem statement directly |
| Bottom-Up Space-Optimized | O(nยฒ) | O(n) | Production โ iterative, minimal memory, no stack-overflow risk |
Common Mistakes
- Greedy neighbor selection: Always picking the smaller adjacent value produces a locally optimal step but not globally โ a cheap cell in one row may lead to expensive cells below.
- Forgetting that adjacency is restricted: From
(row, col)you can only move to(row+1, col)or(row+1, col+1). Moving to(row+1, col-1)is illegal, unlike standard 2D grid problems. - Seeding dp with zeros instead of the last row: Starting with
dp = [0] * ngives incorrect base costs โ the correct seed is a copy of the last row's actual values. - Off-by-one on the row loop: The outer loop must start at
n - 2, notn - 1. Rown - 1is already the base case; re-processing it corruptsdp. - Recursive approach without caching: Without memoization each cell is recomputed up to 2^(n-1) times โ exponential cost. Even a 30-row triangle would trigger over a billion calls.
Related Problems
Minimum Path Sumโ the same bottom-up DP pattern applied to a full rectangular gridUnique Pathsโ counting paths through a grid uses the same 2D โ 1D DP reductionCoin Changeโ classic DP with optimal substructure; good contrast to path-cost DPEdit Distanceโ 2D DP that also benefits from a 1D space optimizationUnique Paths IIโ adds obstacle handling to the grid DP structure