You are given an n x n grid. A driverless car starts at the bottom-left corner (0, 0) and must reach the top-right corner (n-1, n-1). The car can only move either one step East (right, increasing i) or one step North (up, increasing j) at each move. However, the car is not allowed to cross the main diagonal, i.e., it can only visit cells where i >= j at all times.
Task:
Write a function numOfPathsToDest(n) that returns the number of unique valid paths from (0, 0) to (n-1, n-1) under these constraints.
Grid Representation:
Each cell is represented as (i, j) where i is the column (East-West) and j is the row (South-North).
The car may only move in the white squares (where i >= j).
Input: n =4Output: 5Explanation:
There are 5 valid paths:[EEENNN, EENENN, ENEENN, ENENEN, EENNEN]Where 'E'= East,'N'= North. The car can only move in white squares(i >= j).
Input:
n =5Output:
14Explanation:
There are 14 valid paths from(0,0) to (4,4) without crossing the main diagonal(i >= j). Paths are not listed for brevity.
Try all possible paths from the start cell to the end cell, moving only East or North, and only visiting cells where i >= j. At each step, branch into two recursive calls: one moving East, one moving North, if allowed.
classSolution {
public:int numOfPathsToDest(int n) {
returncount(0, 0, n);
}
private:int count(int i, int j, int n) {
if (i >= n || j >= n || i < j) return0;
if (i == n-1&& j == n-1) return1;
returncount(i+1, j, n) + count(i, j+1, n);
}
};
classSolution {
publicintnumOfPathsToDest(int n) {
return count(0, 0, n);
}
privateintcount(int i, int j, int n) {
if (i >= n || j >= n || i < j) return 0;
if (i == n-1 && j == n-1) return 1;
return count(i+1, j, n) + count(i, j+1, n);
}
}
1
2
3
4
5
6
7
8
9
10
classSolution {
funnumOfPathsToDest(n: Int): Int {
funcount(i: Int, j: Int): Int {
if (i >= n || j >= n || i < j) return0if (i == n-1&& j == n-1) return1return count(i+1, j) + count(i, j+1)
}
return count(0, 0)
}
}
1
2
3
4
5
6
7
8
9
classSolution:
defnumOfPathsToDest(self, n: int) -> int:
defcount(i: int, j: int) -> int:
if i >= n or j >= n or i < j:
return0if i == n-1and j == n-1:
return1return count(i+1, j) + count(i, j+1)
return count(0, 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
impl Solution {
pubfnnum_of_paths_to_dest(n: i32) -> i32 {
fncount(i: i32, j: i32, n: i32) -> i32 {
if i >= n || j >= n || i < j {
return0;
}
if i == n-1&& j == n-1 {
return1;
}
count(i+1, j, n) + count(i, j+1, n)
}
count(0, 0, n)
}
}
The brute-force recursion repeats the same subproblems many times. By storing the result of each subproblem (i, j) in a memoization table, we avoid redundant work and make the solution efficient.
#include<vector>classSolution {
public:int numOfPathsToDest(int n) {
std::vector<std::vector<int>> memo(n, std::vector<int>(n, -1));
returncount(0, 0, n, memo);
}
private:int count(int i, int j, int n, std::vector<std::vector<int>>& memo) {
if (i >= n || j >= n || i < j) return0;
if (i == n-1&& j == n-1) return1;
if (memo[i][j] !=-1) return memo[i][j];
return memo[i][j] = count(i+1, j, n, memo) + count(i, j+1, n, memo);
}
};
classSolution {
publicintnumOfPathsToDest(int n) {
int[][] memo =newint[n][n];
for (int[] row : memo) java.util.Arrays.fill(row, -1);
return count(0, 0, n, memo);
}
privateintcount(int i, int j, int n, int[][] memo) {
if (i >= n || j >= n || i < j) return 0;
if (i == n-1 && j == n-1) return 1;
if (memo[i][j]!=-1) return memo[i][j];
return memo[i][j]= count(i+1, j, n, memo) + count(i, j+1, n, memo);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classSolution {
funnumOfPathsToDest(n: Int): Int {
val memo = Array(n) { IntArray(n) { -1 } }
funcount(i: Int, j: Int): Int {
if (i >= n || j >= n || i < j) return0if (i == n-1&& j == n-1) return1if (memo[i][j] != -1) return memo[i][j]
memo[i][j] = count(i+1, j) + count(i, j+1)
return memo[i][j]
}
return count(0, 0)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classSolution:
defnumOfPathsToDest(self, n: int) -> int:
memo = [[-1]*n for _ in range(n)]
defcount(i: int, j: int) -> int:
if i >= n or j >= n or i < j:
return0if i == n-1and j == n-1:
return1if memo[i][j] !=-1:
return memo[i][j]
memo[i][j] = count(i+1, j) + count(i, j+1)
return memo[i][j]
return count(0, 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
impl Solution {
pubfnnum_of_paths_to_dest(n: i32) -> i32 {
fncount(i: i32, j: i32, n: i32, memo: &mut Vec<Vec<i32>>) -> i32 {
if i >= n || j >= n || i < j {
return0;
}
if i == n-1&& j == n-1 {
return1;
}
if memo[i asusize][j asusize] !=-1 {
return memo[i asusize][j asusize];
}
memo[i asusize][j asusize] = count(i+1, j, n, memo) + count(i, j+1, n, memo);
memo[i asusize][j asusize]
}
letmut memo =vec![vec![-1; n asusize]; n asusize];
count(0, 0, n, &mut memo)
}
}
Instead of solving subproblems recursively, we can fill a DP table iteratively from the base cases up, using the allowed moves and diagonal constraint.
Create a 2D DP table dp[i][j] where each cell stores the number of ways to reach (i, j) from (0, 0).
Only fill cells where i >= j (below or on the main diagonal).
The start cell (0, 0) is initialized to 1.
For each cell (i, j), the number of ways to reach it is the sum of ways to reach from the left (i-1, j) and from below (i, j-1), if those cells are valid and satisfy the diagonal constraint.
classSolution {
publicintnumOfPathsToDest(int n) {
int[][] dp =newint[n][n];
dp[0][0]= 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
if (i > 0) dp[i][j]+= dp[i-1][j];
if (j > 0) dp[i][j]+= dp[i][j-1];
}
}
return dp[n-1][n-1];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classSolution {
funnumOfPathsToDest(n: Int): Int {
val dp = Array(n) { IntArray(n) }
dp[0][0] = 1for (i in0 until n) {
for (j in0..i) {
if (i > 0) dp[i][j] += dp[i-1][j]
if (j > 0) dp[i][j] += dp[i][j-1]
}
}
return dp[n-1][n-1]
}
}
1
2
3
4
5
6
7
8
9
10
11
classSolution:
defnumOfPathsToDest(self, n: int) -> int:
dp = [[0]*n for _ in range(n)]
dp[0][0] =1for i in range(n):
for j in range(i+1):
if i >0:
dp[i][j] += dp[i-1][j]
if j >0:
dp[i][j] += dp[i][j-1]
return dp[n-1][n-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
impl Solution {
pubfnnum_of_paths_to_dest(n: i32) -> i32 {
let n = n asusize;
letmut dp =vec![vec![0; n]; n];
dp[0][0] =1;
for i in0..n {
for j in0..=i {
if i >0 {
dp[i][j] += dp[i-1][j];
}
if j >0 {
dp[i][j] += dp[i][j-1];
}
}
}
dp[n-1][n-1]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classSolution {
numOfPathsToDest(n: number):number {
constdp: number[][] = Array.from({length: n}, () => Array(n).fill(0));
dp[0][0] =1;
for (leti=0; i<n; i++) {
for (letj=0; j<=i; j++) {
if (i>0) dp[i][j] +=dp[i-1][j];
if (j>0) dp[i][j] +=dp[i][j-1];
}
}
returndp[n-1][n-1];
}
}