Facebook Pixel

2912. Number of Ways to Reach Destination in the Grid 🔒

Problem Description

You have a grid with dimensions n × m (both 1-indexed), and you need to find the number of ways to move from a starting position source to an ending position dest in exactly k moves.

The movement rules are:

  • From any cell [x₁, y₁], you can move to another cell [x₂, y₂] if either:
    • x₁ == x₂ (same row, different column), OR
    • y₁ == y₂ (same column, different row)
  • You cannot stay in the same cell (both coordinates must not be the same)

Given:

  • n: number of rows in the grid
  • m: number of columns in the grid
  • k: exact number of moves to make
  • source: starting position [x, y] (1-indexed)
  • dest: destination position [x, y] (1-indexed)

Return the number of ways to reach dest from source in exactly k moves, modulo 10⁹ + 7.

For example, in a move from cell [2, 3], you could go to:

  • Any cell in row 2 except [2, 3] itself
  • Any cell in column 3 except [2, 3] itself

The solution uses dynamic programming with state transitions. It tracks four states based on the relationship between current position and source:

  • State 0: at the source position itself
  • State 1: different row, same column as source
  • State 2: same row, different column as source
  • State 3: different row and different column from source

The transitions between states are calculated based on how many valid moves are possible from each state, considering the grid dimensions and movement constraints.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that after any number of moves, the position relative to the source can only be in one of four states:

  1. Back at the source itself
  2. Different row but same column as source
  3. Same row but different column as source
  4. Different row AND different column from source

Why does this matter? Because the number of ways to reach any specific cell depends only on which of these four states it belongs to, not on the exact coordinates. This dramatically reduces the problem complexity.

Consider what happens when we make a move:

  • From the source position, we can move to (n-1) cells in the same column or (m-1) cells in the same row
  • From a position in the same column (but different row), we can return to source, move to (n-2) other cells in that column, or jump to any of (m-1) columns (landing in state 3)
  • Similar logic applies for positions in the same row or in completely different rows/columns

Instead of tracking paths to every possible cell (which would be computationally expensive), we track only how many ways we can reach each of these four states after each move. This transforms an intractable problem into one with just 4 variables that we update k times.

The transition formulas emerge naturally when we count the valid moves from each state:

  • g[0]: Ways to return to source = from state 1 (same column) we have (n-1) such positions, plus from state 2 (same row) we have (m-1) such positions
  • g[1]: Ways to reach same column = from source (1 way) + from within same column (n-2) ways + from different row/column positions that can jump to this column (m-1) ways
  • And so on...

After k iterations, we simply check which state the destination belongs to relative to the source and return the corresponding count.

Learn more about Math, Dynamic Programming and Combinatorics patterns.

Solution Approach

We implement the solution using dynamic programming with state transitions. The approach uses four variables to track the number of ways to reach each state after each move.

State Definition:

  • a (or f[0]): number of ways to be at the source position
  • b (or f[1]): number of ways to be in a different row but same column as source
  • c (or f[2]): number of ways to be in the same row but different column as source
  • d (or f[3]): number of ways to be in a different row AND different column from source

Initialization: We start with a = 1 (we begin at the source), and b = c = d = 0 (we haven't moved yet).

State Transitions: For each of the k moves, we calculate the new state values based on the current state:

aa = (n - 1) × b + (m - 1) × c
bb = a + (n - 2) × b + (m - 1) × d
cc = a + (m - 2) × c + (n - 1) × d
dd = b + c + (n - 2) × d + (m - 2) × d

Breaking down each transition:

  • aa: To reach source, we can come from (n-1) positions in the same column (state b) or (m-1) positions in the same row (state c)
  • bb: To reach a different row in the same column, we can come from source (1 way), from (n-2) other positions in that column, or from (m-1) different columns in different rows
  • cc: To reach a different column in the same row, we can come from source (1 way), from (m-2) other positions in that row, or from (n-1) different rows in different columns
  • dd: To reach a different row and column, we can come from state b (1 way), state c (1 way), or from (n-2) + (m-2) other positions in state d

Modulo Operation: All calculations are performed modulo 10^9 + 7 to prevent overflow.

Final Answer: After k iterations, we determine which state the destination belongs to:

  • If source[0] == dest[0] (same row):
    • If source[1] == dest[1]: return a (destination is source)
    • Else: return c (different column, same row)
  • Else (different row):
    • If source[1] == dest[1]: return b (different row, same column)
    • Else: return d (different row and column)

The time complexity is O(k) and space complexity is O(1) since we only maintain four variables throughout the computation.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's work through a small example with a 3×3 grid, moving from source [1,1] to destination [2,3] in exactly k=2 moves.

Initial Setup:

  • Grid: 3×3 (n=3, m=3)
  • Source: [1,1]
  • Destination: [2,3] (different row AND different column from source)
  • States: a=1, b=0, c=0, d=0 (we start at source)

Move 1: From source [1,1], we can move to:

  • Same column (column 1): [2,1] and [3,1] → 2 positions (state b)
  • Same row (row 1): [1,2] and [1,3] → 2 positions (state c)

Calculating new states:

  • aa = (3-1)×0 + (3-1)×0 = 0 (can't return to source in 1 move)
  • bb = 1 + (3-2)×0 + (3-1)×0 = 1 (1 way from source)
  • cc = 1 + (3-2)×0 + (3-1)×0 = 1 (1 way from source)
  • dd = 0 + 0 + (3-2)×0 + (3-2)×0 = 0 (can't reach different row AND column in 1 move)

After move 1: a=0, b=1, c=1, d=0

This means:

  • 0 ways to be at [1,1] (source)
  • 1 way to be in column 1 but different row (either [2,1] or [3,1])
  • 1 way to be in row 1 but different column (either [1,2] or [1,3])
  • 0 ways to be in different row AND column

Move 2: Now we calculate transitions from the current states:

  • aa = (3-1)×1 + (3-1)×1 = 2 + 2 = 4

    • From state b (different row, same column): 2 positions can reach source
    • From state c (same row, different column): 2 positions can reach source
  • bb = 0 + (3-2)×1 + (3-1)×0 = 0 + 1 + 0 = 1

    • From source: 0 ways (not at source)
    • From within same column: 1 way (can move within column)
    • From state d: 0 ways (no positions in state d)
  • cc = 0 + (3-2)×1 + (3-1)×0 = 0 + 1 + 0 = 1

    • From source: 0 ways
    • From within same row: 1 way
    • From state d: 0 ways
  • dd = 1 + 1 + (3-2)×0 + (3-2)×0 = 2

    • From state b: 1 way (can jump to any different column)
    • From state c: 1 way (can jump to any different row)
    • From state d: 0 ways (no positions there yet)

After move 2: a=4, b=1, c=1, d=2

Final Answer: Destination [2,3] is in a different row AND different column from source [1,1], so it belongs to state d.

Therefore, there are 2 ways to reach [2,3] from [1,1] in exactly 2 moves.

We can verify this by listing the actual paths:

  1. [1,1] → [1,3] → [2,3] (move along row 1, then along column 3)
  2. [1,1] → [2,1] → [2,3] (move along column 1, then along row 2)

The dynamic programming approach correctly counts these paths without explicitly enumerating them, making it efficient even for large values of k.

Solution Implementation

1class Solution:
2    def numberOfWays(
3        self, n: int, m: int, k: int, source: List[int], dest: List[int]
4    ) -> int:
5        MOD = 10**9 + 7
6      
7        # Dynamic programming states after i steps:
8        # same_pos: number of ways to stay at the same position
9        # same_row_diff_col: number of ways to be in same row but different column
10        # diff_row_same_col: number of ways to be in different row but same column
11        # diff_row_diff_col: number of ways to be in different row and different column
12      
13        # Initialize for 0 steps (starting position)
14        same_pos = 1
15        same_row_diff_col = 0
16        diff_row_same_col = 0
17        diff_row_diff_col = 0
18      
19        # Calculate transitions for each step
20        for _ in range(k):
21            # Calculate next state values based on current state
22          
23            # To stay at same position: can come from same row (n-1 choices) or same column (m-1 choices)
24            next_same_pos = ((n - 1) * same_row_diff_col + (m - 1) * diff_row_same_col) % MOD
25          
26            # To reach same row, different column: 
27            # - from same position (1 way to any of m-1 columns)
28            # - from same row different column (n-2 other columns to current)
29            # - from different row different column (m-1 columns in same row)
30            next_same_row_diff_col = (same_pos + (n - 2) * same_row_diff_col + (m - 1) * diff_row_diff_col) % MOD
31          
32            # To reach different row, same column:
33            # - from same position (1 way to any of n-1 rows)
34            # - from different row same column (m-2 other rows to current)
35            # - from different row different column (n-1 rows in same column)
36            next_diff_row_same_col = (same_pos + (m - 2) * diff_row_same_col + (n - 1) * diff_row_diff_col) % MOD
37          
38            # To reach different row, different column:
39            # - from same row different column (1 way to any of n-1 rows)
40            # - from different row same column (1 way to any of m-1 columns)
41            # - from different row different column ((n-2) + (m-2) ways to stay in different state)
42            next_diff_row_diff_col = (same_row_diff_col + diff_row_same_col + (n - 2) * diff_row_diff_col + (m - 2) * diff_row_diff_col) % MOD
43          
44            # Update states for next iteration
45            same_pos = next_same_pos
46            same_row_diff_col = next_same_row_diff_col
47            diff_row_same_col = next_diff_row_same_col
48            diff_row_diff_col = next_diff_row_diff_col
49      
50        # Return the appropriate count based on source and destination relationship
51        if source[0] == dest[0]:  # Same row
52            if source[1] == dest[1]:  # Same position
53                return same_pos
54            else:  # Different column
55                return diff_row_same_col
56        else:  # Different row
57            if source[1] == dest[1]:  # Same column
58                return same_row_diff_col
59            else:  # Different column
60                return diff_row_diff_col
61
1class Solution {
2    public int numberOfWays(int n, int m, int k, int[] source, int[] dest) {
3        final int MOD = 1000000007;
4      
5        // Dynamic programming array to track number of ways for each state
6        // State 0: at source position (same row and column as source)
7        // State 1: same row as source, different column
8        // State 2: different row, same column as source
9        // State 3: different row and different column from source
10        long[] dp = new long[4];
11        dp[0] = 1; // Initially at source position
12      
13        // Perform k transitions
14        while (k-- > 0) {
15            long[] nextDp = new long[4];
16          
17            // Transition to state 0 (at source position)
18            // Can come from state 1 (move within same row) or state 2 (move within same column)
19            nextDp[0] = ((n - 1) * dp[1] + (m - 1) * dp[2]) % MOD;
20          
21            // Transition to state 1 (same row as source, different column)
22            // Can come from state 0 (move to different column in source row)
23            // or state 1 (move within same row but not to source column)
24            // or state 3 (move from different row to source row)
25            nextDp[1] = (dp[0] + (n - 2) * dp[1] + (m - 1) * dp[3]) % MOD;
26          
27            // Transition to state 2 (different row, same column as source)
28            // Can come from state 0 (move to different row in source column)
29            // or state 2 (move within same column but not to source row)
30            // or state 3 (move from different column to source column)
31            nextDp[2] = (dp[0] + (m - 2) * dp[2] + (n - 1) * dp[3]) % MOD;
32          
33            // Transition to state 3 (different row and column from source)
34            // Can come from state 1 (move to different row)
35            // or state 2 (move to different column)
36            // or state 3 (move to another position that's still different from source)
37            nextDp[3] = (dp[1] + dp[2] + (n - 2) * dp[3] + (m - 2) * dp[3]) % MOD;
38          
39            dp = nextDp;
40        }
41      
42        // Determine which state corresponds to the destination position
43        if (source[0] == dest[0]) {
44            // Same row as source
45            return source[1] == dest[1] ? (int) dp[0] : (int) dp[2];
46        }
47        // Different row from source
48        return source[1] == dest[1] ? (int) dp[1] : (int) dp[3];
49    }
50}
51
1class Solution {
2public:
3    int numberOfWays(int n, int m, int k, vector<int>& source, vector<int>& dest) {
4        const int MOD = 1e9 + 7;
5      
6        // Dynamic programming states:
7        // dp[0]: at source position (same row, same column)
8        // dp[1]: same row as source, different column
9        // dp[2]: different row from source, same column
10        // dp[3]: different row and different column from source
11        vector<long long> dp(4);
12        dp[0] = 1;  // Initially at source position
13      
14        // Perform k transitions
15        while (k--) {
16            vector<long long> nextDp(4);
17          
18            // Transitions to state 0 (back to source position):
19            // - From state 1: choose any of (n-1) other rows to move to source column
20            // - From state 2: choose any of (m-1) other columns to move to source row
21            nextDp[0] = ((n - 1) * dp[1] + (m - 1) * dp[2]) % MOD;
22          
23            // Transitions to state 1 (same row, different column):
24            // - From state 0: move to any of (n-1) other rows
25            // - From state 1: stay in same row, move to any of (n-2) other rows (excluding source row)
26            // - From state 3: move to source row from any of (m-1) columns
27            nextDp[1] = (dp[0] + (n - 2) * dp[1] + (m - 1) * dp[3]) % MOD;
28          
29            // Transitions to state 2 (different row, same column):
30            // - From state 0: move to any of (m-1) other columns
31            // - From state 2: stay in same column, move to any of (m-2) other columns (excluding source column)
32            // - From state 3: move to source column from any of (n-1) rows
33            nextDp[2] = (dp[0] + (m - 2) * dp[2] + (n - 1) * dp[3]) % MOD;
34          
35            // Transitions to state 3 (different row, different column):
36            // - From state 1: move to any of (m-2) other columns (not source column)
37            // - From state 2: move to any of (n-2) other rows (not source row)
38            // - From state 3: stay in different row/column, move to (n-2) other rows or (m-2) other columns
39            nextDp[3] = (dp[1] + dp[2] + (n - 2) * dp[3] + (m - 2) * dp[3]) % MOD;
40          
41            dp = move(nextDp);
42        }
43      
44        // Determine which state corresponds to the destination
45        if (source[0] == dest[0]) {  // Same row
46            return source[1] == dest[1] ? dp[0] : dp[2];  // Same position or different column
47        }
48        return source[1] == dest[1] ? dp[1] : dp[3];  // Same column or different row & column
49    }
50};
51
1function numberOfWays(n: number, m: number, k: number, source: number[], dest: number[]): number {
2    const MOD = 1e9 + 7;
3  
4    // Dynamic programming states:
5    // dp[0]: at source position (same row, same column)
6    // dp[1]: same row as source, different column
7    // dp[2]: different row from source, same column
8    // dp[3]: different row and different column from source
9    let dp: number[] = [0, 0, 0, 0];
10    dp[0] = 1;  // Initially at source position
11  
12    // Perform k transitions
13    while (k--) {
14        const nextDp: number[] = [0, 0, 0, 0];
15      
16        // Transitions to state 0 (back to source position):
17        // - From state 1: choose any of (n-1) other rows to move to source column
18        // - From state 2: choose any of (m-1) other columns to move to source row
19        nextDp[0] = ((n - 1) * dp[1] + (m - 1) * dp[2]) % MOD;
20      
21        // Transitions to state 1 (same row, different column):
22        // - From state 0: move to any of (n-1) other rows
23        // - From state 1: stay in same row, move to any of (n-2) other rows (excluding source row)
24        // - From state 3: move to source row from any of (m-1) columns
25        nextDp[1] = (dp[0] + (n - 2) * dp[1] + (m - 1) * dp[3]) % MOD;
26      
27        // Transitions to state 2 (different row, same column):
28        // - From state 0: move to any of (m-1) other columns
29        // - From state 2: stay in same column, move to any of (m-2) other columns (excluding source column)
30        // - From state 3: move to source column from any of (n-1) rows
31        nextDp[2] = (dp[0] + (m - 2) * dp[2] + (n - 1) * dp[3]) % MOD;
32      
33        // Transitions to state 3 (different row, different column):
34        // - From state 1: move to any of (m-2) other columns (not source column)
35        // - From state 2: move to any of (n-2) other rows (not source row)
36        // - From state 3: stay in different row/column, move to (n-2) other rows or (m-2) other columns
37        nextDp[3] = (dp[1] + dp[2] + (n - 2) * dp[3] + (m - 2) * dp[3]) % MOD;
38      
39        dp = nextDp;
40    }
41  
42    // Determine which state corresponds to the destination
43    if (source[0] === dest[0]) {  // Same row
44        return source[1] === dest[1] ? dp[0] : dp[2];  // Same position or different column
45    }
46    return source[1] === dest[1] ? dp[1] : dp[3];  // Same column or different row & column
47}
48

Time and Space Complexity

The time complexity is O(k), where k is the number of moves. This is because the algorithm runs a single loop that iterates exactly k times. In each iteration, it performs a constant number of arithmetic operations (additions, multiplications, and modulo operations) to update the four variables a, b, c, and d. Since each iteration takes O(1) time and there are k iterations, the overall time complexity is O(k).

The space complexity is O(1). The algorithm uses only a fixed number of variables (a, b, c, d, aa, bb, cc, dd, and mod) regardless of the input size. These variables store intermediate results and the final answer, but their number doesn't grow with n, m, or k. The input lists source and dest are given as parameters and don't count toward the extra space used by the algorithm itself. Therefore, the space complexity is constant, or O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect State-to-Variable Mapping

One of the most common mistakes is mixing up which variable corresponds to which state, especially when returning the final answer. The variable names and their meanings can be confusing:

Pitfall Example:

# Incorrect mapping when returning the result
if source[0] == dest[0]:  # Same row
    if source[1] == dest[1]:  # Same position
        return same_pos
    else:  # Different column but same row
        return diff_row_same_col  # WRONG! Should be same_row_diff_col

Solution: Use clear, consistent naming and add comments to track what each variable represents:

# State definitions (relative to source):
# same_pos: at source position
# same_row_diff_col: same row as source, different column
# diff_row_same_col: different row from source, same column  
# diff_row_diff_col: different row and different column from source

2. Incorrect Transition Coefficients

Another critical error is using wrong coefficients in state transitions. For example, when calculating transitions to diff_row_same_col, you might accidentally use (n-2) instead of (m-2):

Pitfall Example:

# Wrong coefficient - using n-2 where m-2 should be used
next_diff_row_same_col = (same_pos + (n - 2) * diff_row_same_col + ...) % MOD
# Should be (m-2) because we're counting different rows while staying in same column

Solution: Remember the logic for each coefficient:

  • When moving within the same row: use m (column count)
  • When moving within the same column: use n (row count)
  • Subtract appropriately based on restrictions (e.g., -1 for excluding current position, -2 for excluding both current and target)

3. Forgetting the Modulo Operation

With large values of k, intermediate calculations can overflow. Forgetting to apply modulo at each step leads to incorrect results:

Pitfall Example:

# Missing modulo - can cause overflow
next_same_pos = (n - 1) * same_row_diff_col + (m - 1) * diff_row_same_col

Solution: Apply modulo after every arithmetic operation:

next_same_pos = ((n - 1) * same_row_diff_col + (m - 1) * diff_row_same_col) % MOD

4. Edge Case: Single Row or Column Grid

When n = 1 or m = 1, some transitions become impossible, and the coefficients can become negative:

Pitfall Example:

# When n = 1, (n-2) becomes -1, causing incorrect calculations
next_same_row_diff_col = (same_pos + (n - 2) * same_row_diff_col + ...) % MOD

Solution: Handle edge cases explicitly or ensure coefficients don't go negative:

# Use max(0, coefficient) to prevent negative values
next_same_row_diff_col = (same_pos + max(0, n - 2) * same_row_diff_col + (m - 1) * diff_row_diff_col) % MOD

5. Zero Moves Edge Case

When k = 0, the answer should be 1 if source equals destination, 0 otherwise. The initialization handles this correctly, but be careful not to enter the loop:

Solution: The current implementation handles this correctly by initializing same_pos = 1 and running the loop exactly k times. If k = 0, the loop doesn't execute, and the final return logic correctly returns 1 for source == dest, 0 otherwise.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More