|
| 1 | +LeetCode 62, "Unique Paths," is a classic problem that often serves as an excellent introduction to dynamic programming. It challenges us to find the number of unique paths a robot can take to reach the bottom-right corner of a `m x n` grid, starting from the top-left corner. The robot can only move either down or right at any point in time. |
| 2 | + |
| 3 | +## Problem Description |
| 4 | + |
| 5 | +Imagine a robot positioned at the top-left cell (0,0) of a grid with `m` rows and `n` columns. The robot's goal is to reach the bottom-right cell (`m-1`, `n-1`). The only allowed moves are one step down or one step right. We need to calculate the total number of distinct paths the robot can take to reach its destination. |
| 6 | + |
| 7 | +Let's visualize a simple `3 x 7` grid: |
| 8 | + |
| 9 | +``` |
| 10 | +S . . . . . . |
| 11 | +. . . . . . . |
| 12 | +. . . . . . F |
| 13 | +``` |
| 14 | + |
| 15 | +Where `S` is the start and `F` is the finish. |
| 16 | + |
| 17 | +## Dynamic Programming Approach |
| 18 | + |
| 19 | +This problem has optimal substructure and overlapping subproblems, making it a perfect candidate for dynamic programming. |
| 20 | + |
| 21 | +Consider a cell `(i, j)` in the grid. To reach this cell, the robot must have come either from the cell directly above it (`i-1, j`) by moving down, or from the cell directly to its left (`i, j-1`) by moving right. |
| 22 | + |
| 23 | +Therefore, the number of unique paths to reach `(i, j)` is the sum of unique paths to reach `(i-1, j)` and unique paths to reach `(i, j-1)`. |
| 24 | + |
| 25 | +Let `dp[i][j]` represent the number of unique paths to reach cell `(i, j)`. |
| 26 | +The recurrence relation is: |
| 27 | +`dp[i][j] = dp[i-1][j] + dp[i][j-1]` |
| 28 | + |
| 29 | +**Base Cases:** |
| 30 | +* For any cell in the first row (`i=0`), there's only one way to reach it: by moving right repeatedly from `(0,0)`. So, `dp[0][j] = 1`. |
| 31 | +* For any cell in the first column (`j=0`), there's only one way to reach it: by moving down repeatedly from `(0,0)`. So, `dp[i][0] = 1`. |
| 32 | +* The starting cell `(0,0)` has `dp[0][0] = 1` path (it's already there). |
| 33 | + |
| 34 | +We can build a 2D array (or even optimize space to a 1D array) to store these path counts. |
| 35 | + |
| 36 | +## Go Solution |
| 37 | + |
| 38 | +Here's an implementation of the dynamic programming approach in Go: |
| 39 | + |
| 40 | +```go |
| 41 | +func uniquePaths(m int, n int) int { |
| 42 | + // Create a 2D DP array initialized with 1s for the base cases |
| 43 | + dp := make([][]int, m) |
| 44 | + for i := range dp { |
| 45 | + dp[i] = make([]int, n) |
| 46 | + } |
| 47 | + |
| 48 | + // Initialize the first row and first column with 1s |
| 49 | + // since there's only one way to reach any cell in the first row/column |
| 50 | + // (by only moving right or only moving down respectively). |
| 51 | + for i := 0; i < m; i++ { |
| 52 | + dp[i][0] = 1 |
| 53 | + } |
| 54 | + for j := 0; j < n; j++ { |
| 55 | + dp[0][j] = 1 |
| 56 | + } |
| 57 | + |
| 58 | + // Fill the DP table |
| 59 | + for i := 1; i < m; i++ { |
| 60 | + for j := 1; j < n; j++ { |
| 61 | + dp[i][j] = dp[i-1][j] + dp[i][j-1] |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + // The result is the value at the bottom-right corner |
| 66 | + return dp[m-1][n-1] |
| 67 | +} |
| 68 | +``` |
| 69 | +### Combinatorial Approach |
| 70 | + |
| 71 | +Alternatively, this problem can be solved using a combinatorial approach. To reach the bottom-right corner of an `m x n` grid, the robot must make exactly `m-1` 'down' moves and `n-1` 'right' moves. The total number of moves will be `(m-1) + (n-1)`. |
| 72 | + |
| 73 | +The problem then reduces to finding the number of ways to arrange these `m-1` down moves and `n-1` right moves. This is a classic combinatorial problem: choosing `m-1` positions for the 'down' moves (or `n-1` positions for the 'right' moves) out of a total of `(m-1) + (n-1)` moves. |
| 74 | + |
| 75 | +The formula for combinations is C(N, K) = N! / (K! * (N-K)!), where N is the total number of steps and K is the number of 'down' (or 'right') moves. |
| 76 | + |
| 77 | +### Go Solution (Combinatorial) |
| 78 | + |
| 79 | +```go |
| 80 | +func uniquePathsCombinatorial(m int, n int) int { |
| 81 | + downMoves := m - 1 |
| 82 | + rightMoves := n - 1 |
| 83 | + totalSteps := downMoves + rightMoves |
| 84 | + |
| 85 | + // Choose the smaller of downMoves or rightMoves for k to minimize calculations |
| 86 | + k := downMoves |
| 87 | + if rightMoves < downMoves { |
| 88 | + k = rightMoves |
| 89 | + } |
| 90 | + |
| 91 | + var comb float64 = 1.0 |
| 92 | + // Formula: C(N, K) = (N/1) * ((N-1)/2) * ... * ((N-k+1)/k) |
| 93 | + // This avoids large factorial calculations by performing multiplications and divisions iteratively. |
| 94 | + for i := 1; i <= k; i++ { |
| 95 | + comb = comb * float64(totalSteps - i + 1) / float64(i) |
| 96 | + } |
| 97 | + |
| 98 | + return int(comb) |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## Conclusion |
| 103 | + |
| 104 | +The "Unique Paths" problem demonstrates the power of dynamic programming in breaking down a complex problem into simpler, overlapping subproblems. By carefully defining our state and recurrence relation, we can build up the solution efficiently. This particular problem also has a combinatorial solution using binomial coefficients, but the dynamic programming approach is often more intuitive for beginners to DP. |
0 commit comments