Skip to content
DSAPrep

    ↑↓ to move · Enter to open · Esc to close

    Medium2-D DP

    Unique Paths

    There is a robot on an m x n grid. The robot is initially located at the top-left corner and tries to move to the bottom-right corner. The robot can only move either down or right at any point in time.

    Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner.

    Example 1

    Input: m = 3, n = 7

    Output: 28

    Example 2

    Input: m = 3, n = 2

    Output: 3

    Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: Right -> Down -> Down, Down -> Down -> Right, Down -> Right -> Down.

    Constraints

    • 1 <= m, n <= 100
    View original on LeetCode ↗

    The number of ways to reach any cell is the number of ways to reach the cell above it, plus the number of ways to reach the cell to its left — those are the only two moves that could have landed you there.

    Brute Force Recursion

    Time O(2^(m+n))Space O(m+n)

    Recurse from the start: at each cell, branch into “move down” and “move right”, and count the paths that reach the bottom-right corner.

    class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
    def paths(r: int, c: int) -> int:
    if r == m - 1 or c == n - 1:
    return 1
    return paths(r + 1, c) + paths(r, c + 1)
    return paths(0, 0)

    Correct, but the same (r, c) is reached by many different paths and gets recomputed from scratch every time — exponential blow-up.

    2-D DP Table (Bottom-Up)

    OptimalTime O(m·n)Space O(m·n)

    Cache the answer for every cell instead of recomputing it. The entire top row and left column are base cases (only one possible path: straight right, or straight down), and every other cell combines the cell above and the cell to the left.

    class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
    dp = [[1] * n for _ in range(m)]
    for r in range(1, m):
    for c in range(1, n):
    dp[r][c] = dp[r - 1][c] + dp[r][c - 1]
    return dp[m - 1][n - 1]

    Filling the table for the first example, m = 3, n = 7:

    Watch how every cell is built from the cell above it and the cell to its left.

    dp[row][col] = above + left

    0
    1
    2
    3
    4
    5
    6
    0
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    2
    1
    1
    1
    1
    1
    1
    1

    row 0 and column 0 = 1 (straight moves only)

    1 / 13
    comparingseenresult

    Base cases: row 0 and column 0 each hold exactly 1 path, since a straight line of moves is the only option.

    Complexity: each of the m·n cells is computed once with O(1) work → O(m·n) time, O(m·n) space for the table.

    1-D Rolling Array

    Time O(m·n)Space O(n)

    Each row of the table only ever depends on the row directly above it. Instead of storing the whole grid, keep a single row and update it in place, left to right — row[c] += row[c-1] reuses the “row above” value that’s still sitting in row[c] before it gets overwritten.

    class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
    row = [1] * n
    for _ in range(1, m):
    for c in range(1, n):
    row[c] += row[c - 1]
    return row[-1]

    Same O(m·n) time, but space drops from the full grid to a single row — O(n) space.

    More 2-D DP practice