231. Unique Grid Paths
Problem
Count paths from the top-left cell to the bottom-right cell of a rows-by-cols grid, moving only one cell right or down. Dimensions are nonnegative. Return zero if either dimension is zero; a one-cell grid has one zero-move path. Return an exact integer.
Examples
Example 1
unique_paths(3,3)
=> 6The final table entry counts six right/down routes.
Example 2
unique_paths(1,1)
=> 1Taking no moves is one complete path.
Example 3
unique_paths(0,4)
=> 0There is no starting cell.
Reveal the worked solution and reasoning
Why this mechanism works
Name both axes before filling the table: dp[r][c] counts paths ending at that cell. The final move comes from above or from the left, and these route classes are disjoint. Boundary cells have only one predecessor; the origin needs an explicit seed of one.
State and boundaries
dp[r][c] is the complete count for exactly cell (r,c). Row-major order completes its top and left predecessors before use.
Protocol
- Handle zero dimensions.
- Allocate a zero table and seed the origin with one.
- Scan cells in row-major order, skipping the origin.
- Add the top count if present and the left count if present.
- Return the destination count.
Visualization
3x3 path-count table
1 1 1
1 2 3
1 3 6
each interior value = top + leftPython implementation
def unique_paths(rows, cols):
if rows == 0 or cols == 0:
return 0
dp = [[0] * cols for _ in range(rows)]
dp[0][0] = 1
for r in range(rows):
for c in range(cols):
if r == c == 0:
continue
dp[r][c] = (dp[r - 1][c] if r else 0) + (dp[r][c - 1] if c else 0)
return dp[-1][-1]
Worked execution
unique_paths(3,3)
L2 unique_paths: if rows == 0 or cols == 0: rows=3; cols=3 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=3; cols=3; _=0 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=3; cols=3; _=1 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=3; cols=3; _=2 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=3; cols=3; dp=[[0, 0, 0], [0, 0, 0], [0, 0, 0]] L5 unique_paths: dp[0][0] = 1 rows=3; cols=3; dp=[[1, 0, 0], [0, 0, 0], [0, 0, 0]] L6 unique_paths: for r in range(rows): rows=3; cols=3; dp=[[1, 0, 0], [0, 0, 0], [0, 0, 0]]; r=0 L7 unique_paths: for c in range(cols): rows=3; cols=3; dp=[[1, 0, 0], [0, 0, 0], [0, 0, 0]]; r=0; c=0 ... 14 intermediate state changes omitted ... L7 unique_paths: for c in range(cols): rows=3; cols=3; dp=[[1, 1, 1], [1, 2, 3], [1, 0, 0]]; r=2; c=1 L10 unique_paths: dp[r][c] = (dp[r - 1][c] if r else 0) + (dp[r][c - 1] if c else 0) rows=3; cols=3; dp=[[1, 1, 1], [1, 2, 3], [1, 3, 0]]; r=2; c=1 L7 unique_paths: for c in range(cols): rows=3; cols=3; dp=[[1, 1, 1], [1, 2, 3], [1, 3, 0]]; r=2; c=2 L10 unique_paths: dp[r][c] = (dp[r - 1][c] if r else 0) + (dp[r][c - 1] if c else 0) rows=3; cols=3; dp=[[1, 1, 1], [1, 2, 3], [1, 3, 6]]; r=2; c=2
Result: 6. The final table entry counts six right/down routes.
unique_paths(1,1)
L2 unique_paths: if rows == 0 or cols == 0: rows=1; cols=1 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=1; cols=1; _=0 L4 unique_paths: dp = [[0] * cols for _ in range(rows)] rows=1; cols=1; dp=[[0]] L5 unique_paths: dp[0][0] = 1 rows=1; cols=1; dp=[[1]] L6 unique_paths: for r in range(rows): rows=1; cols=1; dp=[[1]]; r=0 L7 unique_paths: for c in range(cols): rows=1; cols=1; dp=[[1]]; r=0; c=0
Result: 1. Taking no moves is one complete path.
Why the solution is correct
The origin's empty path is the base. Every path into another cell ends with exactly one of the two permitted moves. Removing that move maps it uniquely to a path counted by the corresponding predecessor. Summing their complete counts therefore counts every route once.
Time and space
O(rows*cols) additions and table entries. Exact counts can require many bits; the table is pedagogically explicit rather than space-compressed.
Common mistakes
Seeding every cell with one and also adding predecessors overcounts. A zero-sized grid has no origin to seed.
Reasoning card
State: dp[r][c] is the complete count for exactly cell (r,c). Row-major order completes its top and left predecessors before use.
Invariant: dp[r][c] counts paths to exactly that cell.
Decision to rehearse: Each cell receives paths from top and left.
Finish: Return the destination count
Transfer exercise
Compress the table to one row, explaining which old and new row values each update reads.