Unique Paths is the friendliest introduction to grid dynamic programming. It looks like a combinatorics puzzle, but the cleanest solution just fills in a table where every cell is the sum of two neighbors.
Problem. A robot sits in the top-left corner of an m × n grid. It can move only right or
down, one step at a time, and wants to reach the bottom-right corner. How many distinct paths are
there?
Example: a 3 × 3 grid → answer 6. There are six different right/down routes from the top-left to
the bottom-right cell.
The slow way first
The brute-force idea is recursion: from any cell, the number of paths is "paths if I go right" plus "paths if I go down." Count both and add them.
That works, but it re-explores the same cells over and over — the recursion tree is exponential. The fix is to notice that the answer for each cell only depends on its right-neighbor and down-neighbor, so we can compute each cell once and store it.
The idea: every cell is above + left
Let dp[i][j] be the number of distinct paths from the start to cell (i, j). Two facts give us the whole table:
- The first row and first column are all
1— there is only one straight-line way to reach them (all rights, or all downs). - Every other cell is reached only from above or from the left, so
dp[i][j] = dp[i-1][j] + dp[i][j-1].
Fill the grid top-to-bottom, left-to-right, and the bottom-right cell holds the final count.
Walk through it
Step through the animation. First the top row and left column are seeded with 1. Then each inner cell lights up its two source cells (above and left) and shows their sum: dp[1][1] = 2, then 3, 3, and finally the corner dp[2][2] = 3 + 3 = 6.
Pseudocode
make an m-by-n grid called "dp", all entries start at 1
for each cell in the first row: it stays 1 (one way: all rights)
for each cell in the first column: it stays 1 (one way: all downs)
for i from 1 to m-1:
for j from 1 to n-1:
dp[i][j] = dp[i-1][j] + dp[i][j-1] # above + left
return dp[m-1][n-1] # the bottom-right cornerThe Python solution
def unique_paths(m, n):
dp = [[1] * n for _ in range(m)]
for j in range(n):
dp[0][j] = 1
for i in range(1, m):
dp[i][0] = 1
for j in range(1, n):
above = dp[i - 1][j]
left = dp[i][j - 1]
dp[i][j] = above + left
return dp[m - 1][n - 1]dpstarts as anm × ngrid of1s, which already takes care of the first row and column.- The first two loops make the seeding explicit: the top row and left column are each
1. - The inner double loop fills every other cell as
above + left— the heart of the recurrence. aboveis the cell directly up,leftis the cell directly to the left.- We return
dp[m-1][n-1], the bottom-right corner, which holds the total path count.
Complexity
| Case | Time | Notes |
|---|---|---|
| Naive recursion | O(2^(m+n)) (moderate) | re-counts the same cells |
| Grid DP (this solution) | O(m·n) (moderate) | each cell computed once |
O(m·n) (moderate)We fill every cell exactly once and each fill is one addition, so the work is O(m·n). The grid itself uses O(m·n) space — and you can shrink that to O(n) by keeping only one row at a time, since each cell only needs the row above.
When this pattern shows up
Whenever a problem asks "how many ways" or "best cost" to reach a cell while moving in fixed directions (right/down), reach for a 2D DP grid where each cell combines its neighbors. Minimum Path Sum, Unique Paths II (with obstacles), and many counting problems are all the same fill-the-grid move.
Get the base cases right: the first row and first column must be 1, not 0. If you leave them at
0, every inner cell sums to 0 and the whole grid collapses.
Practice
In a 3 × 3 grid, once the first row and first column are filled with 1, what is dp[1][1]?
1. What is the value of every cell in the first row and first column?
2. What is the recurrence for an inner cell dp[i][j]?
3. Why is the grid DP O(m·n) instead of exponential?
4. How many unique paths are there in a 3 × 3 grid?