There is an m x n grid where you are allowed to move either down or to the right at any point in time.
Given the two integers m and n, return the number of possible unique paths that can be taken from the top-left corner of the grid (grid[0][0]) to the bottom-right corner (grid[m - 1][n - 1]).
You may assume the output will fit in a 32-bit integer.
Example 1:
Input: m = 3, n = 6
Output: 21Example 2:
Input: m = 3, n = 3
Output: 6Constraints:
1 <= m, n <= 100
You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid.
Try to think in terms of recursion and visualize it as a decision tree, where we have two choices at each step. Can you determine the base condition and recurrence relation?
We recursively traverse the grid using row i and column j. At each step, we explore both possibilities: moving down or moving right, ensuring we do not go out of bounds. If we reach the bottom-right cell, we return 1.
This approach has exponential complexity. Can you think of a way to optimize the recursion? Maybe you should consider using a dynamic programming approach.
We can use memoization to cache the results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to store these results.
This is the pure recursive (brute force) way to think about the problem.
From any cell (i, j) in the grid:
(i, j) is:paths going right + paths going down
Base ideas:
So the problem naturally breaks into smaller subproblems, making recursion a direct fit.
(0, 0).(i, j):(i, j) is the destination (m-1, n-1), return 1.(i, j) is outside the grid, return 0.(i, j + 1)(i + 1, j)(0, 0).Where is the number of rows and is the number of columns.
This is the optimized version of recursion using memoization.
In the brute-force approach, the same cell (i, j) is solved many times.
But the number of paths from a cell never changes, so we can store it once and reuse it.
Think of it this way:
(i, j) asks:This turns an exponential recursion into a polynomial-time solution.
memo[m][n], initialized to -1.dfs(i, j):(i, j) is the destination (m-1, n-1), return 1.(i, j) is out of bounds, return 0.memo[i][j] is already computed, return it.memo[i][j] = dfs(i, j+1) + dfs(i+1, j)memo[i][j].(0, 0).class Solution:
def uniquePaths(self, m: int, n: int) -> int:
memo = [[-1] * n for _ in range(m)]
def dfs(i, j):
if i == (m - 1) and j == (n - 1):
return 1
if i >= m or j >= n:
return 0
if memo[i][j] != -1:
return memo[i][j]
memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j)
return memo[i][j]
return dfs(0, 0)Where is the number of rows and is the number of columns.
Instead of starting from the top and recursing, we build the answer from the destination backward.
From any cell (i, j), the number of unique paths to the destination is:
(i+1, j)(i, j+1)If we already know these values, we can compute the current cell directly.
So we:
1(m+1) x (n+1) DP table initialized with 0.dp[m-1][n-1] = 1 (only one way to stay at destination).m-1 to 0 and columns from n-1 to 0.(i, j): dp[i][j] = dp[i+1][j] + dp[i][j+1]dp[0][0].Where is the number of rows and is the number of columns.
Each cell only depends on:
So instead of storing the entire 2D grid, we can keep just one row at a time.
We update the row from right to left, using values from:
newRow[j + 1])row[j])This reduces space while keeping the same logic.
row of size n with all 1sm - 1 rows:1s.newRow[j] = newRow[j + 1] + row[j]row with newRow.row[0] (top-left cell).Where is the number of rows and is the number of columns.
From any cell, you can reach the destination by moving:
The number of ways to reach a cell equals:
ways from the cell below + ways from the cell to the right
Instead of using a full 2D table, we notice that:
We keep updating this array from right to left, accumulating paths.
dp of size n with all values as 1dp[j] = dp[j] + dp[j + 1]dp[0] contains the total number of unique paths.dp[0].Where is the number of rows and is the number of columns.
To go from the top-left to the bottom-right of an m x n grid, you can only move right or down.
So overall, you make (m + n - 2) moves.
The problem becomes:
In how many different ways can we arrange these right and down moves?
This is a combinations problem:
That gives:
m or n is 1, return 1 (only one path).m and n to reduce calculations.Where is the number of rows and is the number of columns.