Unique Paths [Solution]
Solution 1: Dynamic Programming
This problem assesses your ability to use dynamic programming to efficiently find the number of unique paths in a grid.
Here’s a Python implementation of the unique_paths
function using dynamic programming:
def unique_paths(m: int, n: int) -> int:
# Create a 2D array to store the number of unique paths for each cell
dp = [[0] * n for _ in range(m)]
# Initialize the top row and leftmost column to 1
for i in range(m):
dp[i][0] = 1
for j in range(n):
dp[0][j] = 1
# Fill in the rest of the array using dynamic programming
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[-1][-1] # Number of unique paths for the bottom-right corner
# Example usage:
result1 = unique_paths(3, 7)
print(result1) # Output: 28
result2 = unique_paths(3, 2)
print(result2) # Output: 3
result3 = unique_paths(2, 2)
print(result3) # Output: 2
result4 = unique_paths(1, 1)
print(result4) # Output: 1
This solution uses dynamic programming to fill in a
2D array dp
where dp[i][j]
represents the number of
unique paths to reach the cell at row i
and column j
.
Time Complexity:
The time complexity is O(m * n)
, where m
is the number of rows and n
is the number of columns in the grid.
Space Complexity:
The space complexity is O(m * n)
due to the 2D array.
Solution 2: Backtracking
Using backtracking for this problem is not the most efficient approach because it leads to exponential time complexity. However, for the sake of completeness, here’s an implementation using backtracking:
def unique_paths(m: int, n: int) -> int:
def backtrack(row: int, col: int) -> int:
# Base case: reached the bottom-right corner
if row == m - 1 and col == n - 1:
return 1
# Check if moving down is within bounds
down_paths = 0
if row + 1 < m:
down_paths = backtrack(row + 1, col)
# Check if moving right is within bounds
right_paths = 0
if col + 1 < n:
right_paths = backtrack(row, col + 1)
return down_paths + right_paths
# Start backtracking from the top-left corner
return backtrack(0, 0)
# Example usage:
print(unique_paths(3, 7)) # Output: 28
print(unique_paths(3, 2)) # Output: 3
print(unique_paths(2, 2)) # Output: 2
print(unique_paths(1, 1)) # Output: 1
Explanation:
- We define a recursive function
backtrack
that explores all possible paths from the top-left corner to the bottom-right corner. - At each step, we have two choices: move down or move right. We explore both choices recursively until we reach the bottom-right corner.
- The base case of the recursion is when we reach the bottom-right corner, in which case we return
1
to indicate that we have found a valid path. - We keep track of the number of paths found by summing the paths obtained from moving down and moving right.
Time Complexity:
The time complexity of this solution is exponential, as it explores all possible paths in the worst case. It’s roughly O(2^(m+n)
), where m
is the number of rows and n
is the number of columns. Let’s analyze why it is so:
- Number of Recursive Calls:
- At each step of the recursion, we have two choices: to move down or to move right.
- Therefore, each recursive call results in branching into two recursive calls.
- As a result, the number of recursive calls grows exponentially with the size of the input grid.
- Recursion Depth:
- The recursion depth corresponds to the number of steps needed to reach the bottom-right corner of the grid.
- In the worst case, the robot needs to move
m - 1
steps downwards andn - 1
steps to the right to reach the destination. - Thus, the recursion depth is
m + n - 2
.
- Combination of Factors:
- Since each recursive call branches into two calls, and the recursion depth is
m + n - 2
, the total number of recursive calls made is roughly2^(m+n-2)
. - Therefore, the time complexity of the backtracking solution is exponential, approximately O(
2^(m+n)
) wherem
is the number of rows andn
is the number of columns in the grid.
- Since each recursive call branches into two calls, and the recursion depth is
In summary, the backtracking solution explores all possible paths from the top-left corner to the bottom-right corner of the grid, leading to an exponential time complexity. As a result, it is not suitable for large grids due to its inefficiency.
Space Complexity:
The space complexity is O(m + n
) due to the recursion depth, as the maximum depth of the recursion tree is bounded by the sum of m
and n
.
Solution 3: Combinatorics
Another solution to find the number of unique paths is to use combinatorics.
We can observe that to reach the bottom-right corner from the top-left corner,
the robot must move m-1
steps downwards and n-1
steps to the right,
for a total of m-1 + n-1 = m + n - 2
steps.
Out of these m + n - 2
steps, we need to choose m - 1
steps to move downwards, and the remaining steps will be to move to the right. Therefore, the number of unique paths is the number of combinations of choosing m - 1
out of m + n - 2
steps.
We can use the formula for combinations (n
choose k
) to compute this:
C(n, k) = n! / (k! * (n - k)!)
import math
def unique_paths(m: int, n: int) -> int:
# Calculate the number of unique paths using combinatorics formula
return math.comb(m + n - 2, m - 1)
# Example usage:
print(unique_paths(3, 7)) # Output: 28
print(unique_paths(3, 2)) # Output: 3
print(unique_paths(2, 2)) # Output: 2
print(unique_paths(1, 1)) # Output: 1
Explanation:
- We use the
math.comb
function from themath
module to compute the number of combinations of choosingm - 1
out ofm + n - 2
steps. - This directly gives us the number of unique paths from the top-left corner to the bottom-right corner of the grid.
Applying the same logic, we can consider it as selecting n - 1
steps to move to the right, leaving the rest of the steps to move downwards:
import math
def unique_paths(m: int, n: int) -> int:
# Calculate the number of unique paths using combinatorics formula
return math.comb(m + n - 2, n - 1)
# Example usage:
print(unique_paths(3, 7)) # Output: 28
print(unique_paths(3, 2)) # Output: 3
print(unique_paths(2, 2)) # Output: 2
print(unique_paths(1, 1)) # Output: 1
Time Complexity:
The time complexity of this solution is O(1
), as it involves simple arithmetic operations to compute the result.
Space Complexity:
The space complexity is also O(1
), as we are not using any additional data structures that depend on the input size.