Geek in a Maze

Last Updated : 23 Aug, 2026

Given a maze mat[][] of size n × m, where each cell is either:

  • '.' representing an empty cell, or
  • '#' representing an obstacle.

Find the number of distinct empty cells that Geek can visit starting from the cell (r, c).

  • Geek can move to any of the four adjacent cells (up, down, left, or right) from a cell, provided that the adjacent cell lies inside the maze and is not an obstacle.
  • Geek can make at most u upward moves and at most d downward on a path starting from [r, c]. Note that there can be multiple paths geek can follow to cover maximum distinct cells.
  • There is no limit on the number of left or right moves.
  • If the starting cell is an obstacle, return 0.

Examples:

Input: r = 1, c = 0, u = 1, d = 1, mat = [['.', '.', '.'], ['.', '#', '.'], ['#', '.', '.']]

781

Output: 5
Explanation: Geek starts from (1, 0) and follows the path (1,0)->(0,0)->(0,1)->(0,2)->(1,2). The cells (1,1) and (2,0) are obstacles, so they cannot be visited. Hence, Geek can visit 5 distinct empty cells.

Input: r = 2, c = 1, u = 2, d = 2, mat = [['.', '.', '.'], ['.', '#', '.'], ['.', '.', '.']]

782

Output: 8
Explanation: Geek starts from (2, 1) and follows the path (2,1)->(2,2)->(1,2)->(0,2)->(0,1)->(0,0)->(1,0)->(2,0). The cell (1,1) is an obstacle, so it cannot be visited. Hence, Geek can visit all 8 empty cells.

Try It Yourself
redirect icon

[Naive Approach] Using Depth First Search(DFS) - O(n * m * u * d) Time and O(n * m * u * d) Space

The idea is to explore the maze using DFS and try all four possible moves from every cell. Since the same cell can be reached with different numbers of upward and downward moves remaining, the state must include both resources along with the cell position. Therefore, we use solve(r, c, uLeft, dLeft) and mark each complete state as visited. A separate reachable matrix records whether a cell has been reached through at least one valid state.

  • Start DFS from (r, c) with u upward and d downward moves available.
  • Treat (r, c, uLeft, dLeft) as the complete state and mark it as visited.
  • Mark the current cell as reachable.
  • Recursively move up and down after decreasing their respective move limits.
  • Recursively move left and right without changing the move limits.
  • Finally, count all cells marked as reachable.
C++
#include <bits/stdc++.h>
using namespace std;

// DFS using the complete state:
// (row, column, remaining upward moves, remaining downward moves)
void dfs(int r, int c, int uLeft, int dLeft, vector<vector<char>> &mat,
         vector<vector<vector<vector<bool>>>> &visited, vector<vector<bool>> &reachable)
{

    int n = mat.size();
    int m = mat[0].size();

    // Check for invalid cell or obstacle.
    if (r < 0 || r >= n || c < 0 || c >= m || mat[r][c] == '#')
    {
        return;
    }

    // If this exact state has already been processed.
    if (visited[r][c][uLeft][dLeft])
    {
        return;
    }

    // Mark this state as visited.
    visited[r][c][uLeft][dLeft] = true;

    // The current cell is reachable.
    reachable[r][c] = true;

    // Move Up: consume one upward move.
    if (uLeft > 0)
    {
        dfs(r - 1, c, uLeft - 1, dLeft, mat, visited, reachable);
    }

    // Move Down: consume one downward move.
    if (dLeft > 0)
    {
        dfs(r + 1, c, uLeft, dLeft - 1, mat, visited, reachable);
    }

    // Move Left: no upward/downward move consumed.
    dfs(r, c - 1, uLeft, dLeft, mat, visited, reachable);

    // Move Right: no upward/downward move consumed.
    dfs(r, c + 1, uLeft, dLeft, mat, visited, reachable);
}

// Returns the number of distinct cells that can be visited.
int numberOfCells(int r, int c, int u, int d, vector<vector<char>> &mat)
{

    int n = mat.size();
    int m = mat[0].size();

    // If starting cell is an obstacle.
    if (mat[r][c] == '#')
    {
        return 0;
    }

    // visited[r][c][uLeft][dLeft]
    vector<vector<vector<vector<bool>>>> visited(
        n, vector<vector<vector<bool>>>(m, vector<vector<bool>>(u + 1, vector<bool>(d + 1, false))));

    // Stores whether each cell can be reached.
    vector<vector<bool>> reachable(n, vector<bool>(m, false));

    // Start DFS with all moves available.
    dfs(r, c, u, d, mat, visited, reachable);

    // Count distinct reachable cells.
    int ans = 0;

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (reachable[i][j])
            {
                ans++;
            }
        }
    }

    return ans;
}

int main()
{
    vector<vector<char>> mat = {{'.', '.', '.'}, {'.', '#', '.'}, {'#', '.', '.'}};

    int r = 1;
    int c = 0;
    int u = 1;
    int d = 1;

    cout << numberOfCells(r, c, u, d, mat) << endl;

    return 0;
}
Java
class GFG {

    // DFS using state:
    // (row, column, remaining upward moves, remaining
    // downward moves)
    static void dfs(int r, int c, int uLeft, int dLeft,
                    char[][] mat, boolean[][][][] visited,
                    boolean[][] reachable)
    {
        int n = mat.length;
        int m = mat[0].length;

        // Invalid cell or obstacle.
        if (r < 0 || r >= n || c < 0 || c >= m
            || mat[r][c] == '#') {
            return;
        }

        // Same state has already been processed.
        if (visited[r][c][uLeft][dLeft]) {
            return;
        }

        // Mark this state as visited.
        visited[r][c][uLeft][dLeft] = true;

        // Mark cell as reachable.
        reachable[r][c] = true;

        // Move Up.
        if (uLeft > 0) {
            dfs(r - 1, c, uLeft - 1, dLeft, mat, visited,
                reachable);
        }

        // Move Down.
        if (dLeft > 0) {
            dfs(r + 1, c, uLeft, dLeft - 1, mat, visited,
                reachable);
        }

        // Move Left.
        dfs(r, c - 1, uLeft, dLeft, mat, visited,
            reachable);

        // Move Right.
        dfs(r, c + 1, uLeft, dLeft, mat, visited,
            reachable);
    }

    static int numberOfCells(int r, int c, int u, int d,
                             char[][] mat)
    {

        int n = mat.length;
        int m = mat[0].length;

        // Starting cell is blocked.
        if (mat[r][c] == '#') {
            return 0;
        }

        // visited[row][col][remainingUp][remainingDown]
        boolean[][][][] visited
            = new boolean[n][m][u + 1][d + 1];

        // Stores distinct reachable cells.
        boolean[][] reachable = new boolean[n][m];

        // Start DFS.
        dfs(r, c, u, d, mat, visited, reachable);

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (reachable[i][j]) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void main(String[] args)
    {
        char[][] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;
        int u = 1;
        int d = 1;

        System.out.println(numberOfCells(r, c, u, d, mat));
    }
}
Python
# DFS using state:
# (row, column, remaining upward moves, remaining downward moves)
def dfs(r, c, u_left, d_left, mat, visited, reachable):

    n = len(mat)
    m = len(mat[0])

    # Invalid cell or obstacle.
    if (r < 0 or r >= n or c < 0 or c >= m
            or mat[r][c] == '#'):
        return

    # Same state has already been processed.
    if visited[r][c][u_left][d_left]:
        return

    # Mark this state as visited.
    visited[r][c][u_left][d_left] = True

    # Mark cell as reachable.
    reachable[r][c] = True

    # Move Up.
    if u_left > 0:
        dfs(r - 1, c, u_left - 1, d_left,
            mat, visited, reachable)

    # Move Down.
    if d_left > 0:
        dfs(r + 1, c, u_left, d_left - 1,
            mat, visited, reachable)

    # Move Left.
    dfs(r, c - 1, u_left, d_left,
        mat, visited, reachable)

    # Move Right.
    dfs(r, c + 1, u_left, d_left,
        mat, visited, reachable)


# Returns the number of distinct reachable cells.
def numberOfCells(r, c, u, d, mat):

    n = len(mat)
    m = len(mat[0])

    # Starting cell is blocked.
    if mat[r][c] == '#':
        return 0

    # visited[row][col][remainingUp][remainingDown]
    visited = [
        [
            [
                [False for _ in range(d + 1)]
                for _ in range(u + 1)
            ]
            for _ in range(m)
        ]
        for _ in range(n)
    ]

    # Stores distinct reachable cells.
    reachable = [
        [False for _ in range(m)]
        for _ in range(n)
    ]

    # Start DFS.
    dfs(r, c, u, d, mat, visited, reachable)

    # Count reachable cells.
    ans = 0

    for i in range(n):
        for j in range(m):
            if reachable[i][j]:
                ans += 1

    return ans


# Driver Code
if __name__ == "__main__":

    mat = [
        ['.', '.', '.'],
        ['.', '#', '.'],
        ['#', '.', '.']
    ]

    r = 1
    c = 0
    u = 1
    d = 1

    print(numberOfCells(r, c, u, d, mat))
C#
using System;

class GFG {

    // DFS using state:
    // (row, column, remaining upward moves, remaining
    // downward moves)
    static void Dfs(int r, int c, int uLeft, int dLeft,
                     char[, ] mat, bool[, , , ] visited,
                     bool[, ] reachable)
    {

        int n = mat.GetLength(0);
        int m = mat.GetLength(1);

        // Invalid cell or obstacle.
        if (r < 0 || r >= n || c < 0 || c >= m
            || mat[r, c] == '#') {
            return;
        }

        // Same state has already been processed.
        if (visited[r, c, uLeft, dLeft]) {
            return;
        }

        // Mark this state as visited.
        visited[r, c, uLeft, dLeft] = true;

        // Mark cell as reachable.
        reachable[r, c] = true;

        // Move Up.
        if (uLeft > 0) {
            Dfs(r - 1, c, uLeft - 1, dLeft, mat, visited,
                reachable);
        }

        // Move Down.
        if (dLeft > 0) {
            Dfs(r + 1, c, uLeft, dLeft - 1, mat, visited,
                reachable);
        }

        // Move Left.
        Dfs(r, c - 1, uLeft, dLeft, mat, visited,
            reachable);

        // Move Right.
        Dfs(r, c + 1, uLeft, dLeft, mat, visited,
            reachable);
    }

    static int numberOfCells(int r, int c, int u, int d,
                             char[, ] mat)
    {
        int n = mat.GetLength(0);
        int m = mat.GetLength(1);

        // Starting cell is blocked.
        if (mat[r, c] == '#') {
            return 0;
        }

        // visited[row][col][remainingUp][remainingDown]
        bool[, , , ] visited = new bool[n, m, u + 1, d + 1];

        // Stores distinct reachable cells.
        bool[, ] reachable = new bool[n, m];

        // Start DFS.
        Dfs(r, c, u, d, mat, visited, reachable);

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (reachable[i, j]) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void Main()
    {
        char[, ] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;
        int u = 1;
        int d = 1;

        Console.WriteLine(numberOfCells(r, c, u, d, mat));
    }
}
JavaScript
// DFS using state:
// (row, column, remaining upward moves, remaining downward moves)
function dfs(r, c, uLeft, dLeft, mat, visited, reachable) {

    const n = mat.length;
    const m = mat[0].length;

    // Invalid cell or obstacle.
    if (r < 0 || r >= n || c < 0 || c >= m ||
        mat[r][c] === '#') {
        return;
    }

    // Same state has already been processed.
    if (visited[r][c][uLeft][dLeft]) {
        return;
    }

    // Mark this state as visited.
    visited[r][c][uLeft][dLeft] = true;

    // Mark cell as reachable.
    reachable[r][c] = true;

    // Move Up.
    if (uLeft > 0) {
        dfs(
            r - 1, c,
            uLeft - 1, dLeft,
            mat, visited, reachable
        );
    }

    // Move Down.
    if (dLeft > 0) {
        dfs(
            r + 1, c,
            uLeft, dLeft - 1,
            mat, visited, reachable
        );
    }

    // Move Left.
    dfs(
        r, c - 1,
        uLeft, dLeft,
        mat, visited, reachable
    );

    // Move Right.
    dfs(
        r, c + 1,
        uLeft, dLeft,
        mat, visited, reachable
    );
}


// Returns the number of distinct reachable cells.
function numberOfCells(r, c, u, d, mat) {

    const n = mat.length;
    const m = mat[0].length;

    // Starting cell is blocked.
    if (mat[r][c] === '#') {
        return 0;
    }

    // visited[row][col][remainingUp][remainingDown]
    const visited = Array.from(
        { length: n },
        () => Array.from(
            { length: m },
            () => Array.from(
                { length: u + 1 },
                () => Array(d + 1).fill(false)
            )
        )
    );

    // Stores distinct reachable cells.
    const reachable = Array.from(
        { length: n },
        () => Array(m).fill(false)
    );

    // Start DFS.
    dfs(
        r, c, u, d,
        mat, visited, reachable
    );

    // Count reachable cells.
    let ans = 0;

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (reachable[i][j]) {
                ans++;
            }
        }
    }

    return ans;
}


// Driver Code
const mat = [
    ['.', '.', '.'],
    ['.', '#', '.'],
    ['#', '.', '.']
];

const r = 1;
const c = 0;
const u = 1;
const d = 1;

console.log(numberOfCells(r, c, u, d, mat));

Output
5

Note: The above DFS approach uses a 4D visited array of size n × m × (u + 1) × (d + 1). Although this correctly tracks the state of the DFS, it can require a very large amount of memory when n, m, u, or d are large. As a result, it may cause an Out of Memory (SIGABRT) error on large test cases. Therefore, this approach should be avoided when the constraints are large.

[Better Approach] Using Priority Queue - O(n * m * log(n * m)) Time and O(n * m) Space

Instead of storing uLeft and dLeft as part of a 4D state, the idea is to use a priority queue to process cells in the order of the smallest number of upward moves used, followed by downward moves. For a fixed destination cell, the number of up and down moves are related by its row, so a state with fewer upward moves is always preferable. Therefore, the first time a cell is reached, we can mark it visited and avoid processing it again.

  • Initialize a min-priority queue with the starting state (0, 0, r, c).
  • Store (upUsed, downUsed, row, col) and prioritize fewer upward, then fewer downward moves.
  • For an upward/downward move, increase the corresponding move count if its limit is not exceeded.
  • For left/right moves, keep both move counts unchanged.
  • Mark a cell visited when it is first inserted into the priority queue.
  • Finally, count all visited cells.
C++
#include <bits/stdc++.h>
using namespace std;

// Check whether a cell lies inside the maze.
bool isValid(int r, int c, int n, int m)
{
    return r >= 0 && r < n && c >= 0 && c < m;
}

// Returns the number of distinct cells Geek can visit.
int numberOfCells(int r, int c, int u, int d, vector<vector<char>> &mat)
{
    int n = mat.size();
    int m = mat[0].size();

    // Starting cell is blocked.
    if (mat[r][c] == '#')
    {
        return 0;
    }

    /*
        State stored in priority queue:
        {upUsed, downUsed, row, col}

        Priority:
        1. Smaller upUsed
        2. Smaller downUsed
    */
    priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;

    // visited[row][col] tells whether the cell
    // has already been reached.
    vector<vector<bool>> visited(n, vector<bool>(m, false));

    // Start from the given cell.
    pq.push({0, 0, r, c});
    visited[r][c] = true;

    while (!pq.empty())
    {
        vector<int> current = pq.top();
        pq.pop();

        int upUsed = current[0];
        int downUsed = current[1];
        int x = current[2];
        int y = current[3];

        // Move Up.
        if (isValid(x - 1, y, n, m) && !visited[x - 1][y] && mat[x - 1][y] == '.' && upUsed < u)
        {
            visited[x - 1][y] = true;

            pq.push({upUsed + 1, downUsed, x - 1, y});
        }

        // Move Down.
        if (isValid(x + 1, y, n, m) && !visited[x + 1][y] && mat[x + 1][y] == '.' && downUsed < d)
        {
            visited[x + 1][y] = true;

            pq.push({upUsed, downUsed + 1, x + 1, y});
        }

        // Move Left.
        if (isValid(x, y - 1, n, m) && !visited[x][y - 1] && mat[x][y - 1] == '.')
        {
            visited[x][y - 1] = true;

            pq.push({upUsed, downUsed, x, y - 1});
        }

        // Move Right.
        if (isValid(x, y + 1, n, m) && !visited[x][y + 1] && mat[x][y + 1] == '.')
        {
            visited[x][y + 1] = true;

            pq.push({upUsed, downUsed, x, y + 1});
        }
    }

    // Count all reachable cells.
    int ans = 0;

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (visited[i][j])
            {
                ans++;
            }
        }
    }

    return ans;
}

int main()
{
    vector<vector<char>> mat = {{'.', '.', '.'}, {'.', '#', '.'}, {'#', '.', '.'}};

    int r = 1;
    int c = 0;
    int u = 1;
    int d = 1;

    cout << numberOfCells(r, c, u, d, mat) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {

    // Check whether a cell is inside the maze.
    static boolean isValid(int r, int c, int n, int m)
    {
        return r >= 0 && r < n && c >= 0 && c < m;
    }

    // Returns the number of distinct cells Geek can visit.
    static int numberOfCells(int r, int c, int u, int d,
                             char[][] mat)
    {
        int n = mat.length;
        int m = mat[0].length;

        // Starting cell is blocked.
        if (mat[r][c] == '#') {
            return 0;
        }

        /*
         * State:
         * {upUsed, downUsed, row, col}
         *
         * Priority:
         * 1. Smaller upUsed
         * 2. Smaller downUsed
         */
        PriorityQueue<int[]> pq
            = new PriorityQueue<>((a, b) -> {
                  if (a[0] != b[0])
                      return Integer.compare(a[0], b[0]);

                  return Integer.compare(a[1], b[1]);
              });

        // visited[row][col]
        boolean[][] visited = new boolean[n][m];

        // Start from the given cell.
        pq.offer(new int[] { 0, 0, r, c });
        visited[r][c] = true;

        while (!pq.isEmpty()) {

            int[] current = pq.poll();

            int upUsed = current[0];
            int downUsed = current[1];
            int x = current[2];
            int y = current[3];

            // Move Up.
            if (isValid(x - 1, y, n, m)
                && !visited[x - 1][y]
                && mat[x - 1][y] == '.' && upUsed < u) {

                visited[x - 1][y] = true;

                pq.offer(new int[] { upUsed + 1, downUsed,
                                     x - 1, y });
            }

            // Move Down.
            if (isValid(x + 1, y, n, m)
                && !visited[x + 1][y]
                && mat[x + 1][y] == '.' && downUsed < d) {

                visited[x + 1][y] = true;

                pq.offer(new int[] { upUsed, downUsed + 1,
                                     x + 1, y });
            }

            // Move Left.
            if (isValid(x, y - 1, n, m)
                && !visited[x][y - 1]
                && mat[x][y - 1] == '.') {

                visited[x][y - 1] = true;

                pq.offer(new int[] { upUsed, downUsed, x,
                                     y - 1 });
            }

            // Move Right.
            if (isValid(x, y + 1, n, m)
                && !visited[x][y + 1]
                && mat[x][y + 1] == '.') {

                visited[x][y + 1] = true;

                pq.offer(new int[] { upUsed, downUsed, x,
                                     y + 1 });
            }
        }

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (visited[i][j]) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void main(String[] args)
    {
        char[][] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;
        int u = 1;
        int d = 1;

        System.out.println(numberOfCells(r, c, u, d, mat));
    }
}
Python
import heapq

# Check whether a cell is inside the maze.
def isValid(r, c, n, m):
    return 0 <= r < n and 0 <= c < m

# Returns the number of distinct cells Geek can visit.
def numberOfCells(r, c, u, d, mat):

    n = len(mat)
    m = len(mat[0])

    # Starting cell is blocked.
    if mat[r][c] == '#':
        return 0

    # Min heap.
    # State: (upUsed, downUsed, row, col)
    pq = []

    # Start from the given cell.
    heapq.heappush(pq, (0, 0, r, c))

    # visited[row][col]
    visited = [
        [False] * m
        for _ in range(n)
    ]

    visited[r][c] = True

    while pq:

        upUsed, downUsed, x, y = heapq.heappop(pq)

        # Move Up.
        if (isValid(x - 1, y, n, m)
                and not visited[x - 1][y]
                and mat[x - 1][y] == '.'
                and upUsed < u):

            visited[x - 1][y] = True

            heapq.heappush(
                pq,
                (upUsed + 1, downUsed, x - 1, y)
            )

        # Move Down.
        if (isValid(x + 1, y, n, m)
                and not visited[x + 1][y]
                and mat[x + 1][y] == '.'
                and downUsed < d):

            visited[x + 1][y] = True

            heapq.heappush(
                pq,
                (upUsed, downUsed + 1, x + 1, y)
            )

        # Move Left.
        if (isValid(x, y - 1, n, m)
                and not visited[x][y - 1]
                and mat[x][y - 1] == '.'):

            visited[x][y - 1] = True

            heapq.heappush(
                pq,
                (upUsed, downUsed, x, y - 1)
            )

        # Move Right.
        if (isValid(x, y + 1, n, m)
                and not visited[x][y + 1]
                and mat[x][y + 1] == '.'):

            visited[x][y + 1] = True

            heapq.heappush(
                pq,
                (upUsed, downUsed, x, y + 1)
            )

    # Count reachable cells.
    ans = 0

    for i in range(n):
        for j in range(m):
            if visited[i][j]:
                ans += 1

    return ans


# Driver Code
if __name__ == "__main__":

    mat = [
        ['.', '.', '.'],
        ['.', '#', '.'],
        ['#', '.', '.']
    ]

    r = 1
    c = 0
    u = 1
    d = 1

    print(numberOfCells(r, c, u, d, mat))
C#
using System;
using System.Collections.Generic;

class GFG {

    // State used by the priority queue.
    // {upUsed, downUsed, row, col}
    class State : IComparable<State> {

        public int up;
        public int down;
        public int row;
        public int col;

        public State(int up, int down, int row, int col)
        {
            this.up = up;
            this.down = down;
            this.row = row;
            this.col = col;
        }

        // Smaller up is preferred.
        // If equal, smaller down is preferred.
        public int CompareTo(State other)
        {
            if (up != other.up)
                return up.CompareTo(other.up);

            return down.CompareTo(other.down);
        }
    }

    // Check whether a cell is inside the maze.
    static bool IsValid(int r, int c, int n, int m)
    {
        return r >= 0 && r < n && c >= 0 && c < m;
    }

    // Returns the number of distinct cells Geek can visit.
    static int numberOfCells(int r, int c, int u, int d,
                             char[, ] mat)
    {
        int n = mat.GetLength(0);
        int m = mat.GetLength(1);

        // Starting cell is blocked.
        if (mat[r, c] == '#') {
            return 0;
        }

        // Min priority queue.
        var pq = new PriorityQueue<State, (int, int)>();

        // visited[row, col]
        bool[, ] visited = new bool[n, m];

        // Start from the given cell.
        pq.Enqueue(new State(0, 0, r, c), (0, 0));

        visited[r, c] = true;

        while (pq.Count > 0) {

            State current = pq.Dequeue();

            int upUsed = current.up;
            int downUsed = current.down;
            int x = current.row;
            int y = current.col;

            // Move Up.
            if (IsValid(x - 1, y, n, m)
                && !visited[x - 1, y]
                && mat[x - 1, y] == '.' && upUsed < u) {

                visited[x - 1, y] = true;

                pq.Enqueue(new State(upUsed + 1, downUsed,
                                     x - 1, y),
                           (upUsed + 1, downUsed));
            }

            // Move Down.
            if (IsValid(x + 1, y, n, m)
                && !visited[x + 1, y]
                && mat[x + 1, y] == '.' && downUsed < d) {

                visited[x + 1, y] = true;

                pq.Enqueue(new State(upUsed, downUsed + 1,
                                     x + 1, y),
                           (upUsed, downUsed + 1));
            }

            // Move Left.
            if (IsValid(x, y - 1, n, m)
                && !visited[x, y - 1]
                && mat[x, y - 1] == '.') {

                visited[x, y - 1] = true;

                pq.Enqueue(
                    new State(upUsed, downUsed, x, y - 1),
                    (upUsed, downUsed));
            }

            // Move Right.
            if (IsValid(x, y + 1, n, m)
                && !visited[x, y + 1]
                && mat[x, y + 1] == '.') {

                visited[x, y + 1] = true;

                pq.Enqueue(
                    new State(upUsed, downUsed, x, y + 1),
                    (upUsed, downUsed));
            }
        }

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (visited[i, j]) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void Main()
    {
        char[, ] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;
        int u = 1;
        int d = 1;

        Console.WriteLine(numberOfCells(r, c, u, d, mat));
    }
}
JavaScript
// MinHeap implementation.
class MinHeap {

    constructor() { this.heap = []; }

    // Compare two states.
    // State = [upUsed, downUsed, row, col]
    compare(a, b)
    {
        if (a[0] !== b[0]) {
            return a[0] - b[0];
        }

        return a[1] - b[1];
    }

    // Insert an element into the heap.
    push(value)
    {
        this.heap.push(value);

        let i = this.heap.length - 1;

        while (i > 0) {

            let parent = Math.floor((i - 1) / 2);

            if (this.compare(this.heap[parent],
                             this.heap[i])
                <= 0) {
                break;
            }

            [this.heap[parent], this.heap[i]] =
                [ this.heap[i], this.heap[parent] ];

            i = parent;
        }
    }

    // Remove and return the minimum element.
    pop()
    {
        if (this.heap.length === 0) {
            return null;
        }

        const root = this.heap[0];
        const last = this.heap.pop();

        if (this.heap.length > 0) {

            this.heap[0] = last;

            let i = 0;

            while (true) {

                let left = 2 * i + 1;
                let right = 2 * i + 2;
                let smallest = i;

                if (left < this.heap.length
                    && this.compare(this.heap[left],
                                    this.heap[smallest])
                           < 0) {
                    smallest = left;
                }

                if (right < this.heap.length
                    && this.compare(this.heap[right],
                                    this.heap[smallest])
                           < 0) {
                    smallest = right;
                }

                if (smallest === i) {
                    break;
                }

                [this.heap[i], this.heap[smallest]] =
                    [ this.heap[smallest], this.heap[i] ];

                i = smallest;
            }
        }

        return root;
    }

    // Check whether the heap is empty.
    isEmpty() { return this.heap.length === 0; }
}

// Check whether a cell is inside the maze.
function isValid(r, c, n, m)
{
    return r >= 0 && r < n && c >= 0 && c < m;
}

// Returns the number of distinct cells Geek can visit.
function numberOfCells(r, c, u, d, mat)
{
    const n = mat.length;
    const m = mat[0].length;

    // Starting cell is blocked.
    if (mat[r][c] === "#") {
        return 0;
    }

    const pq = new MinHeap();

    // visited[row][col]
    const visited = Array.from({length : n},
                               () => Array(m).fill(false));

    // Start from the given cell.
    // State = [upUsed, downUsed, row, col]
    pq.push([ 0, 0, r, c ]);
    visited[r][c] = true;

    while (!pq.isEmpty()) {

        const [upUsed, downUsed, x, y] = pq.pop();

        // Move Up.
        if (isValid(x - 1, y, n, m) && !visited[x - 1][y]
            && mat[x - 1][y] === "." && upUsed < u) {
            visited[x - 1][y] = true;

            pq.push([ upUsed + 1, downUsed, x - 1, y ]);
        }

        // Move Down.
        if (isValid(x + 1, y, n, m) && !visited[x + 1][y]
            && mat[x + 1][y] === "." && downUsed < d) {
            visited[x + 1][y] = true;

            pq.push([ upUsed, downUsed + 1, x + 1, y ]);
        }

        // Move Left.
        if (isValid(x, y - 1, n, m) && !visited[x][y - 1]
            && mat[x][y - 1] === ".") {
            visited[x][y - 1] = true;

            pq.push([ upUsed, downUsed, x, y - 1 ]);
        }

        // Move Right.
        if (isValid(x, y + 1, n, m) && !visited[x][y + 1]
            && mat[x][y + 1] === ".") {
            visited[x][y + 1] = true;

            pq.push([ upUsed, downUsed, x, y + 1 ]);
        }
    }

    // Count reachable cells.
    let ans = 0;

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {

            if (visited[i][j]) {
                ans++;
            }
        }
    }

    return ans;
}

// Driver Code
const mat = [
    [ ".", ".", "." ], [ ".", "#", "." ], [ "#", ".", "." ]
];

const r = 1;
const c = 0;
const u = 1;
const d = 1;

console.log(numberOfCells(r, c, u, d, mat));

Output
5

[Expected Approach] BFS with Minimum Up Moves - O(n * m) Time and O(n * m) Space

For every cell, store the minimum number of upward moves needed to reach it. Since moving up is the only movement that consumes the resource we need to minimize, reaching a cell with fewer upward moves always leaves us with at least as much flexibility for future moves. The number of downward moves can then be calculated from the row difference. Therefore, each cell only needs one best state instead of the huge 4D state used by the naive DFS.

  • Create upUsed[][], where each cell stores the minimum upward moves required to reach it.
  • Start BFS from (r, c) with upUsed[r][c] = 0.
  • For the current cell (x, y), calculate downUsed = upUsed[x][y] + (x - r).
  • Move to neighboring cells only when the corresponding up/down limit is satisfied and the new state improves upUsed.
  • Left/right moves keep upUsed unchanged, while moving up increases it by 1.
  • Count all cells whose upUsed value is not INT_MAX.
C++
#include <bits/stdc++.h>
using namespace std;

// Returns the number of distinct cells Geek can visit.
int numberOfCells(int r, int c, int u, int d, vector<vector<char>> &mat)
{
    int n = mat.size();
    int m = mat[0].size();
    
    // Starting cell is blocked.
    if (mat[r][c] == '#')
    {
        return 0;
    }

    /*
        upUsed[i][j] = minimum number of upward moves
        required to reach cell (i, j).
    */
    vector<vector<int>> upUsed(n, vector<int>(m, INT_MAX));

    queue<pair<int, int>> q;

    // Starting cell.
    upUsed[r][c] = 0;
    q.push({r, c});

    while (!q.empty())
    {
        auto [x, y] = q.front();
        q.pop();

        // Number of upward moves used so far.
        int currUp = upUsed[x][y];

        /*
            From:

                downUsed - upUsed = currentRow - startRow

            Therefore:

                downUsed = currUp + (x - r)
        */
        int currDown = currUp + (x - r);

        // Move Up.
        if (x - 1 >= 0 && mat[x - 1][y] == '.' && currUp + 1 <= u && currUp + 1 < upUsed[x - 1][y])
        {
            upUsed[x - 1][y] = currUp + 1;
            q.push({x - 1, y});
        }

        // Move Down.
        if (x + 1 < n && mat[x + 1][y] == '.' && currDown + 1 <= d && currUp < upUsed[x + 1][y])
        {
            upUsed[x + 1][y] = currUp;
            q.push({x + 1, y});
        }

        // Move Left.
        if (y - 1 >= 0 && mat[x][y - 1] == '.' && currUp < upUsed[x][y - 1])
        {
            upUsed[x][y - 1] = currUp;
            q.push({x, y - 1});
        }

        // Move Right.
        if (y + 1 < m && mat[x][y + 1] == '.' && currUp < upUsed[x][y + 1])
        {
            upUsed[x][y + 1] = currUp;
            q.push({x, y + 1});
        }
    }

    // Count reachable cells.
    int ans = 0;

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (upUsed[i][j] != INT_MAX)
            {
                ans++;
            }
        }
    }

    return ans;
}

int main()
{
    vector<vector<char>> mat = {{'.', '.', '.'}, {'.', '#', '.'}, {'#', '.', '.'}};
    
    int r = 1;
    int c = 0;

    int u = 1;
    int d = 1;

    cout << numberOfCells(r, c, u, d, mat) << endl;

    return 0;
}
Java
import java.util.*;

class GFG {

    // Returns the number of distinct cells Geek can visit.
    static int numberOfCells(int r, int c, int u, int d,
                             char[][] mat)
    {
        int n = mat.length;
        int m = mat[0].length;

        // Starting cell is blocked.
        if (mat[r][c] == '#') {
            return 0;
        }

        /*
         * upUsed[i][j] = minimum number of upward moves
         * required to reach cell (i, j).
         */
        int[][] upUsed = new int[n][m];

        for (int i = 0; i < n; i++) {
            Arrays.fill(upUsed[i], Integer.MAX_VALUE);
        }

        Queue<int[]> q = new LinkedList<>();

        // Starting cell.
        upUsed[r][c] = 0;
        q.offer(new int[] { r, c });

        while (!q.isEmpty()) {

            int[] current = q.poll();

            int x = current[0];
            int y = current[1];

            // Number of upward moves used so far.
            int currUp = upUsed[x][y];

            /*
             * downUsed - upUsed = currentRow - startRow
             *
             * Therefore:
             *
             * downUsed = currUp + (x - r)
             */
            int currDown = currUp + (x - r);

            // Move Up.
            if (x - 1 >= 0 && mat[x - 1][y] == '.'
                && currUp + 1 <= u
                && currUp + 1 < upUsed[x - 1][y]) {

                upUsed[x - 1][y] = currUp + 1;

                q.offer(new int[] { x - 1, y });
            }

            // Move Down.
            if (x + 1 < n && mat[x + 1][y] == '.'
                && currDown + 1 <= d
                && currUp < upUsed[x + 1][y]) {

                upUsed[x + 1][y] = currUp;

                q.offer(new int[] { x + 1, y });
            }

            // Move Left.
            if (y - 1 >= 0 && mat[x][y - 1] == '.'
                && currUp < upUsed[x][y - 1]) {

                upUsed[x][y - 1] = currUp;

                q.offer(new int[] { x, y - 1 });
            }

            // Move Right.
            if (y + 1 < m && mat[x][y + 1] == '.'
                && currUp < upUsed[x][y + 1]) {

                upUsed[x][y + 1] = currUp;

                q.offer(new int[] { x, y + 1 });
            }
        }

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {

                if (upUsed[i][j] != Integer.MAX_VALUE) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void main(String[] args)
    {
        char[][] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;

        int u = 1;
        int d = 1;

        System.out.println(numberOfCells(r, c, u, d, mat));
    }
}
Python
from collections import deque

# Returns the number of distinct cells Geek can visit.
def numberOfCells(r, c, u, d, mat):

    n = len(mat)
    m = len(mat[0])

    # Starting cell is blocked.
    if mat[r][c] == '#':
        return 0

    # upUsed[i][j] = minimum number of upward moves
    # required to reach cell (i, j).
    upUsed = [
        [float('inf')] * m
        for _ in range(n)
    ]

    q = deque()

    # Starting cell.
    upUsed[r][c] = 0
    q.append((r, c))

    while q:

        x, y = q.popleft()

        # Number of upward moves used so far.
        currUp = upUsed[x][y]

        # downUsed = currUp + (x - r)
        currDown = currUp + (x - r)

        # Move Up.
        if (x - 1 >= 0 and
            mat[x - 1][y] == '.' and
            currUp + 1 <= u and
                currUp + 1 < upUsed[x - 1][y]):

            upUsed[x - 1][y] = currUp + 1

            q.append((x - 1, y))

        # Move Down.
        if (x + 1 < n and
            mat[x + 1][y] == '.' and
            currDown + 1 <= d and
                currUp < upUsed[x + 1][y]):

            upUsed[x + 1][y] = currUp

            q.append((x + 1, y))

        # Move Left.
        if (y - 1 >= 0 and
            mat[x][y - 1] == '.' and
                currUp < upUsed[x][y - 1]):

            upUsed[x][y - 1] = currUp

            q.append((x, y - 1))

        # Move Right.
        if (y + 1 < m and
            mat[x][y + 1] == '.' and
                currUp < upUsed[x][y + 1]):

            upUsed[x][y + 1] = currUp

            q.append((x, y + 1))

    # Count reachable cells.
    ans = 0

    for i in range(n):
        for j in range(m):

            if upUsed[i][j] != float('inf'):
                ans += 1

    return ans


# Driver Code
if __name__ == "__main__":

    mat = [
        ['.', '.', '.'],
        ['.', '#', '.'],
        ['#', '.', '.']
    ]

    r = 1
    c = 0

    u = 1
    d = 1

    print(numberOfCells(r, c, u, d, mat))
C#
using System;
using System.Collections.Generic;

class GFG {

    // Returns the number of distinct cells Geek can visit.
    static int numberOfCells(int r, int c, int u, int d,
                             char[, ] mat)
    {
        int n = mat.GetLength(0);
        int m = mat.GetLength(1);

        // Starting cell is blocked.
        if (mat[r, c] == '#') {
            return 0;
        }

        /*
         * upUsed[i,j] = minimum number of upward moves
         * required to reach cell (i, j).
         */
        int[, ] upUsed = new int[n, m];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                upUsed[i, j] = int.MaxValue;
            }
        }

        Queue<(int, int)> q = new Queue<(int, int)>();

        // Starting cell.
        upUsed[r, c] = 0;
        q.Enqueue((r, c));

        while (q.Count > 0) {

            var current = q.Dequeue();

            int x = current.Item1;
            int y = current.Item2;

            // Number of upward moves used so far.
            int currUp = upUsed[x, y];

            /*
             * downUsed - upUsed = currentRow - startRow
             *
             * Therefore:
             *
             * downUsed = currUp + (x - r)
             */
            int currDown = currUp + (x - r);

            // Move Up.
            if (x - 1 >= 0 && mat[x - 1, y] == '.'
                && currUp + 1 <= u
                && currUp + 1 < upUsed[x - 1, y]) {

                upUsed[x - 1, y] = currUp + 1;

                q.Enqueue((x - 1, y));
            }

            // Move Down.
            if (x + 1 < n && mat[x + 1, y] == '.'
                && currDown + 1 <= d
                && currUp < upUsed[x + 1, y]) {

                upUsed[x + 1, y] = currUp;

                q.Enqueue((x + 1, y));
            }

            // Move Left.
            if (y - 1 >= 0 && mat[x, y - 1] == '.'
                && currUp < upUsed[x, y - 1]) {

                upUsed[x, y - 1] = currUp;

                q.Enqueue((x, y - 1));
            }

            // Move Right.
            if (y + 1 < m && mat[x, y + 1] == '.'
                && currUp < upUsed[x, y + 1]) {

                upUsed[x, y + 1] = currUp;

                q.Enqueue((x, y + 1));
            }
        }

        // Count reachable cells.
        int ans = 0;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {

                if (upUsed[i, j] != int.MaxValue) {
                    ans++;
                }
            }
        }

        return ans;
    }

    // Main function
    public static void Main()
    {
        char[, ] mat = { { '.', '.', '.' },
                         { '.', '#', '.' },
                         { '#', '.', '.' } };

        int r = 1;
        int c = 0;

        int u = 1;
        int d = 1;

        Console.WriteLine(numberOfCells(r, c, u, d, mat));
    }
}
JavaScript
// Returns the number of distinct cells Geek can visit.
function numberOfCells(r, c, u, d, mat)
{
    const n = mat.length;
    const m = mat[0].length;

    // Starting cell is blocked.
    if (mat[r][c] === "#") {
        return 0;
    }

    /*
        upUsed[i][j] = minimum number of upward moves
        required to reach cell (i, j).
    */
    const upUsed = Array.from(
        {length : n}, () => Array(m).fill(Infinity));

    /*
        Queue implemented using an array and a pointer
        to avoid repeatedly removing the first element.
    */
    const q = [];

    let front = 0;

    // Starting cell.
    upUsed[r][c] = 0;
    q.push([ r, c ]);

    while (front < q.length) {

        const [x, y] = q[front++];

        // Number of upward moves used so far.
        const currUp = upUsed[x][y];

        /*
            downUsed = currUp + (x - r)
        */
        const currDown = currUp + (x - r);

        // Move Up.
        if (x - 1 >= 0 && mat[x - 1][y] === "."
            && currUp + 1 <= u
            && currUp + 1 < upUsed[x - 1][y]) {
            upUsed[x - 1][y] = currUp + 1;

            q.push([ x - 1, y ]);
        }

        // Move Down.
        if (x + 1 < n && mat[x + 1][y] === "."
            && currDown + 1 <= d
            && currUp < upUsed[x + 1][y]) {
            upUsed[x + 1][y] = currUp;

            q.push([ x + 1, y ]);
        }

        // Move Left.
        if (y - 1 >= 0 && mat[x][y - 1] === "."
            && currUp < upUsed[x][y - 1]) {
            upUsed[x][y - 1] = currUp;

            q.push([ x, y - 1 ]);
        }

        // Move Right.
        if (y + 1 < m && mat[x][y + 1] === "."
            && currUp < upUsed[x][y + 1]) {
            upUsed[x][y + 1] = currUp;

            q.push([ x, y + 1 ]);
        }
    }

    // Count reachable cells.
    let ans = 0;

    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {

            if (upUsed[i][j] !== Infinity) {
                ans++;
            }
        }
    }

    return ans;
}

// Driver Code
const mat = [
    [ ".", ".", "." ], [ ".", "#", "." ], [ "#", ".", "." ]
];

const r = 1;
const c = 0;

const u = 1;
const d = 1;

console.log(numberOfCells(r, c, u, d, mat));

Output
5
Comment