Unique Paths

Medium
21 days ago

There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). 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. The test cases are generated so that the answer will be less than or equal to 2 * 10<sup>9</sup>. For example, if m = 3 and n = 7, the expected output is 28. Explain your approach, provide code, and analyze the time and space complexity. Also discuss edge cases and alternative solutions using combinatorics.

Sample Answer

Unique Paths

This problem asks us to find the number of unique paths a robot can take to reach the bottom-right corner of an m x n grid, given that the robot can only move down or right.

Brute Force Solution (Recursion)

A naive approach is to use recursion. We can define a recursive function that takes the current row and column as input. The base cases are:

  1. If we reach the bottom-right corner, we found a path (return 1).
  2. If we go out of bounds, it's not a valid path (return 0).

Otherwise, we recursively call the function by moving down and moving right, and return the sum of the results.

def unique_paths_recursive(m, n):
 def solve(row, col):
 if row == m - 1 and col == n - 1:
 return 1
 if row >= m or col >= n:
 return 0
 return solve(row + 1, col) + solve(row, col + 1)

 return solve(0, 0)

# Example usage:
m = 3
n = 7
result = unique_paths_recursive(m, n)
print(f"Number of unique paths for m={m}, n={n}: {result}") # Output: 28

This brute-force recursive solution has overlapping subproblems, leading to exponential time complexity. It will result in TLE (Time Limit Exceeded) for larger values of m and n.

Optimal Solution (Dynamic Programming)

We can optimize this using dynamic programming. We create a 2D array dp of size m x n, where dp[i][j] stores the number of unique paths to reach cell (i, j). We initialize the first row and first column with 1 because there's only one way to reach any cell in the first row or first column.

Then, we iterate through the rest of the cells, and the number of unique paths to reach dp[i][j] is the sum of the unique paths to reach dp[i-1][j] (moving down) and dp[i][j-1] (moving right).

def unique_paths_dp(m, n):
 dp = [[0] * n for _ in range(m)]

 # Initialize first row and first column to 1
 for i in range(m):
 dp[i][0] = 1
 for j in range(n):
 dp[0][j] = 1

 # Calculate unique paths for the rest of the cells
 for i in range(1, m):
 for j in range(1, n):
 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

 return dp[m - 1][n - 1]

# Example usage:
m = 3
n = 7
result = unique_paths_dp(m, n)
print(f"Number of unique paths for m={m}, n={n}: {result}") # Output: 28

Big(O) Run-time Analysis

The dynamic programming solution has a time complexity of O(m * n), where m is the number of rows and n is the number of columns in the grid. This is because we iterate through each cell in the m x n grid once to calculate the number of unique paths.

The recursive solution will have exponential time complexity due to the overlapping subproblems and repeated calculations.

Big(O) Space Usage Analysis

The dynamic programming solution has a space complexity of O(m * n) because we use a 2D array dp of size m x n to store the number of unique paths for each cell. We can further optimize this to O(n) by only storing the previous row to calculate number of paths.

The recursive solution has space complexity of O(m + n) due to the recursive call stack.

Edge Cases

  1. m = 1, n = 1: There's only one path (stay put).
  2. m = 1, n > 1: There's only one path (move right n-1 times).
  3. m > 1, n = 1: There's only one path (move down m-1 times).
  4. m = 0 or n = 0: No grid exists; return 0 (or handle the exception as appropriate).

These edge cases are handled correctly by both the recursive and dynamic programming solutions.

Alternative Solution (Combinatorics)

This problem can also be solved using combinatorics. The robot needs to move a total of m + n - 2 steps, where m - 1 steps are down and n - 1 steps are right. The number of unique paths is equivalent to choosing m - 1 down steps from the total m + n - 2 steps, which can be calculated using the binomial coefficient:

C(m + n - 2, m - 1) = (m + n - 2)! / ((m - 1)! * (n - 1)!)

This can be implemented using factorials or by iteratively calculating the binomial coefficient to avoid large factorials.