Given positive integers m and n, consider a grid containing m rows and n columns. A robot begins at cell (0, 0) and must reach cell (m - 1, n - 1).
Only a right move or a down move is allowed from any cell. Return the number of distinct valid paths from the starting cell to the destination.
Example 1
Input: m = 3, n = 3
Output: 6
Explanation: To reach cell (2, 2), every valid path requires two Right (R) moves and two Down (D) moves. The 6 possible paths are: RRDD, RDRD, RDDR, DRRD, DRDR, and DDRR. Therefore, the total number of unique paths is 6.
Example 2
Input: m = 1, n = 4
Output: 1
Explanation: To reach cell (0, 3), the robot can only move Right (R) three times. The only valid path is RRR, so the total number of unique paths is 1.
Recursion
Every cell leaves at most two useful choices: move down or move right. Either choice creates a smaller version of the same counting problem, so recursion follows the grid naturally.
Let solve(row, column) represent the number of valid paths from cell (row, column) to the destination. A destination arrival contributes one path, while a boundary crossing contributes none. Counting starts at (0, 0) because every required path begins at the top-left cell.
Algorithm
Begin with a recursive state
solve(row, column)so every call counts paths from one selected cell to the destination.Return
1at cell(m - 1, n - 1)because reaching the destination completes exactly one valid path.Return
0after crossing the bottom or right boundary because an outside position cannot reach the destination.Explore
solve(row + 1, column)so every path beginning with a down move is counted.Explore
solve(row, column + 1)so every path beginning with a right move is counted.Add both results because down-first and right-first routes form separate path groups.
Start recursion from
(0, 0)because the required journey begins at the top-left cell.
Dry Run
Diagram 1
Solution
#include <bits/stdc++.h>using namespace std;class Solution {private: // Counts paths from a cell to the destination. int solve(int row, int column, int m, int n) { // Reaching the target completes one valid route. if (row == m - 1 && column == n - 1) { return 1; } // Crossing a boundary cannot reach the target. if (row >= m || column >= n) { return 0; } // A down move advances to the next row. int downPaths = solve(row + 1, column, m, n); // A right move advances to the next column. int rightPaths = solve(row, column + 1, m, n); // Both move groups contain different routes. return downPaths + rightPaths; }public: // Returns the number of unique grid paths. int uniquePaths(int m, int n) { // Every valid path begins at the top-left cell. return solve(0, 0, m, n); }};// Driver codeint main() { int m = 3; int n = 3; Solution obj; cout << obj.uniquePaths(m, n) << endl; return 0;}Note: Direct recursion may fail for large input values. Repeated subproblems create exponential work, so an online judge may report Time Limit Exceeded.
Complexity Analysis
Time Complexity: O(2(M + N)), where M is the number of rows and N is the number of columns, because each non-terminal state can branch into two recursive calls across at most M + N - 2 moves.
Space Complexity: O(M + N), because the deepest recursion path can contain at most M + N - 1 active calls.
Memoization
Direct recursion reaches the same cell through many move orders. Every repeated arrival asks for the same remaining path count, so recalculation adds work without adding new information.
A two-dimensional array named dp stores the answer for every calculated (row, column) state. Recursion keeps the original choices and base cases, while a stored value returns immediately during later visits.
Algorithm
Begin with an
mbynarray nameddp, filled with-1, so every untouched entry marks an uncalculated state.Keep
solve(row, column)as the path count from one cell to the destination so the recursive meaning remains unchanged.Return
1at the destination and0outside the grid because completed and invalid routes need no cache lookup.Return
dp[row][column]whenever a stored value exists because every later visit has the same remaining choices.Explore the down and right states only for an uncached cell so each valid state performs useful work once.
Store the sum of both move groups in
dp[row][column]because the cached value must represent every path from the selected cell.Start the memoized recursion at
(0, 0)because the cached answer for the starting state solves the complete problem.
Dry Run
Diagram 1
Solution
#include <bits/stdc++.h>using namespace std;class Solution {private: // Counts paths and stores calculated grid states. int solve( int row, int column, int m, int n, vector<vector<int>>& dp ) { // Reaching the target completes one valid route. if (row == m - 1 && column == n - 1) { return 1; } // Crossing a boundary cannot reach the target. if (row >= m || column >= n) { return 0; } // A stored count removes repeated exploration. if (dp[row][column] != -1) { return dp[row][column]; } // A down move advances to the next row. int downPaths = solve(row + 1, column, m, n, dp); // A right move advances to the next column. int rightPaths = solve(row, column + 1, m, n, dp); // Cache stores both disjoint move groups. dp[row][column] = downPaths + rightPaths; // Cached count represents the selected cell. return dp[row][column]; }public: // Returns the number of unique grid paths. int uniquePaths(int m, int n) { // Minus one marks every uncalculated state. vector<vector<int>> dp(m, vector<int>(n, -1)); // Counting begins from the required start. return solve(0, 0, m, n, dp); }};// Driver codeint main() { int m = 3; int n = 3; Solution obj; cout << obj.uniquePaths(m, n) << endl; return 0;}Complexity Analysis
Time Complexity: O(M × N), where M is the number of rows and N is the number of columns, because each of the M × N grid states is computed once with constant work.
Space Complexity: O(M × N), because the dp table stores M × N values, while the recursion stack adds at most O(M + N) space.
Tabulation
Memoization still relies on recursive calls. Tabulation removes the call stack by calculating the same solve(row, column) states in an order keeping every required future state ready.
Each state needs the cell below and the cell to the right. Filling rows from bottom to top and columns from right to left makes both values available before the current state is calculated.
Algorithm
Begin with an
mbynarray nameddp, filled with0, so missing lower or right neighbors naturally contribute no paths.Store
dp[m - 1][n - 1] = 1because the destination state represents one completed path.Traverse rows from
m - 1down to0so every lower-row answer is ready before an upper row needs the answer.Traverse columns from
n - 1down to0so every right-side answer in the current row is already available.Preserve the destination value instead of recalculating the base state because the destination already represents a completed route.
Add the lower and right counts for every other cell because both next moves create distinct path groups.
Return
dp[0][0]because the top-left state represents the required starting position.
Dry Run
Grid Unique Path Tabulation
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: // Builds answers for every valid starting cell. int uniquePaths(int m, int n) { // Zero handles every missing neighboring cell. vector<vector<int>> dp(m, vector<int>(n, 0)); // The destination contains one complete path. dp[m - 1][n - 1] = 1; // Reverse row order keeps lower states ready. for (int row = m - 1; row >= 0; row--) { // Reverse columns keep right states ready. for (int column = n - 1; column >= 0; column--) { // Preserve the known destination base value. if (row == m - 1 && column == n - 1) { continue; } // A missing lower cell contributes no path. int downPaths = row + 1 < m ? dp[row + 1][column] : 0; // A missing right cell contributes no path. int rightPaths = column + 1 < n ? dp[row][column + 1] : 0; // Both valid next moves form distinct paths. dp[row][column] = downPaths + rightPaths; } } // The top-left state represents the full grid. return dp[0][0]; }};// Driver codeint main() { int m = 3; int n = 3; Solution obj; cout << obj.uniquePaths(m, n) << endl; return 0;}Complexity Analysis
Time Complexity: O(M × N), where M is the number of rows and N is the number of columns, because every grid cell is processed once with constant work.
Space Complexity: O(M × N), because the dp table stores one path count for every grid cell, while the iterative approach uses no recursion stack.
Space Optimization
Tabulation reveals a smaller storage need. A cell uses only the value directly below from the completed next row and the value directly right from the current row.
Arrays nextRow and currentRow preserve exactly the required values. Each current count adds the lower and right answers, and the completed currentRow becomes nextRow only after every column has been processed.
Algorithm
Begin with
nextRowfilled with0so positions below the bottom boundary contribute no paths.Traverse rows from bottom to top because
nextRowmust hold the completed row directly below the current row.Create a fresh zero-filled
currentRowfor each row so right-side values can be built without overwriting lower-row answers.Traverse columns from right to left because
currentRow[column + 1]must be ready before calculating the current cell.Store
1at the destination state because reaching the bottom-right cell completes one valid path.Calculate
currentby addingnextRow[column]and the available right-side value because both moves form distinct route groups.Shift
currentRowintonextRowafter the whole row is complete so no lower-row value is replaced early, then returnnextRow[0]after the top row becomes the retained row.
Dry Run
Grid Unique Path Space Optimization
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: // Counts paths with two retained grid rows. int uniquePaths(int m, int n) { // Zero represents positions below the grid. vector<int> nextRow(n, 0); // Reverse row order keeps lower values ready. for (int row = m - 1; row >= 0; row--) { // A fresh row protects lower-row values. vector<int> currentRow(n, 0); // Reverse columns keep right values ready. for (int column = n - 1; column >= 0; column--) { // The destination completes one route. if (row == m - 1 && column == n - 1) { currentRow[column] = 1; continue; } // The retained lower row supplies down paths. int downPaths = nextRow[column]; // A missing right cell contributes no path. int rightPaths = column + 1 < n ? currentRow[column + 1] : 0; // Current combines the two distinct moves. int current = downPaths + rightPaths; // Store the count for the current grid cell. currentRow[column] = current; } // Shift only after the complete row is ready. nextRow = currentRow; } // The retained top row contains the full answer. return nextRow[0]; }};// Driver codeint main() { int m = 3; int n = 3; Solution obj; cout << obj.uniquePaths(m, n) << endl; return 0;}Complexity Analysis
Time Complexity: O(M × N), where M is the number of rows and N is the number of columns, because every grid cell is processed once with constant work.
Space Complexity: O(N), because nextRow and currentRow each store N values, while all older rows are discarded.
Interview follow-up Questions
Yes. Only one movement direction remains available, so a single straight sequence reaches the destination.
Be the first to add a comment.