{x}
blog image

Count Unguarded Cells in the Grid

You are given two integers m and n representing a 0-indexed m x n grid. You are also given two 2D integer arrays guards and walls where guards[i] = [rowi, coli] and walls[j] = [rowj, colj] represent the positions of the ith guard and jth wall respectively.

A guard can see every cell in the four cardinal directions (north, east, south, or west) starting from their position unless obstructed by a wall or another guard. A cell is guarded if there is at least one guard that can see it.

Return the number of unoccupied cells that are not guarded.

 

Example 1:

Input: m = 4, n = 6, guards = [[0,0],[1,1],[2,3]], walls = [[0,1],[2,2],[1,4]]
Output: 7
Explanation: The guarded and unguarded cells are shown in red and green respectively in the above diagram.
There are a total of 7 unguarded cells, so we return 7.

Example 2:

Input: m = 3, n = 3, guards = [[1,1]], walls = [[0,1],[1,0],[2,1],[1,2]]
Output: 4
Explanation: The unguarded cells are shown in green in the above diagram.
There are a total of 4 unguarded cells, so we return 4.

 

Constraints:

  • 1 <= m, n <= 105
  • 2 <= m * n <= 105
  • 1 <= guards.length, walls.length <= 5 * 104
  • 2 <= guards.length + walls.length <= m * n
  • guards[i].length == walls[j].length == 2
  • 0 <= rowi, rowj < m
  • 0 <= coli, colj < n
  • All the positions in guards and walls are unique.

Solution Explanation: Count Unguarded Cells in the Grid

This problem involves finding the number of unguarded cells in a grid given the positions of guards and walls. Guards can see in four directions (north, east, south, west) until obstructed by a wall or another guard.

Approach 1: Simulation

This approach uses a simulation to determine which cells are guarded. We represent the grid using a 2D array.

  1. Initialization: Create a grid g of size m x n filled with zeros (0 represents unguarded, 1 represents guarded, 2 represents wall/guard). Mark the positions of guards and walls with 2 in g.

  2. Guard Simulation: Iterate through each guard's position. For each guard, simulate its line of sight in each of the four directions (up, down, left, right). In each direction:

    • Move to the next cell in that direction.
    • If the cell is within bounds and is not a wall or another guard (value < 2), mark it as guarded (set to 1) and continue to the next cell in that direction.
    • Stop when you encounter a wall or go out of bounds.
  3. Unguarded Cell Count: Finally, iterate through the grid g. Count the number of cells with a value of 0; these are the unguarded cells. Return this count.

Time and Space Complexity Analysis (Approach 1)

  • Time Complexity: O(m*n) - In the worst case, we might traverse every cell in the grid during the guard simulation.
  • Space Complexity: O(m*n) - We use a 2D array g to represent the grid, which has a size of m x n.

Approach 2: DFS + Simulation (More concise but conceptually similar)

This approach uses Depth-First Search (DFS) to simulate the line of sight of each guard.

  1. Initialization: Similar to Approach 1, create a grid and mark guard and wall positions as 2.

  2. DFS Function: A recursive DFS function takes the current cell coordinates, the direction vector (dx, dy), and recursively explores the line of sight in that direction, marking guarded cells as 1. It stops when it encounters a wall or boundary.

  3. Guard Traversal: Iterate through guards and, for each guard, call the DFS function for all four directions.

  4. Unguarded Cell Count: Calculate the number of unguarded cells by subtracting the number of guards, walls, and guarded cells from the total number of cells (m*n).

Time and Space Complexity Analysis (Approach 2)

  • Time Complexity: O(m*n) - Similar to Approach 1, we still visit each cell at most once.
  • Space Complexity: O(m*n) - The grid mtx dominates the space complexity. The recursive call stack of the DFS is at most O(min(m,n)) in depth.

Code Examples (Approach 1 - Simulation):

Python:

def countUnguarded(m, n, guards, walls):
    grid = [[0] * n for _ in range(m)]
    for r, c in guards:
        grid[r][c] = 2
    for r, c in walls:
        grid[r][c] = 2
    for r, c in guards:
        for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            nr, nc = r + dr, c + dc
            while 0 <= nr < m and 0 <= nc < n and grid[nr][nc] < 2:
                grid[nr][nc] = 1
                nr += dr
                nc += dc
    return sum(sum(cell == 0 for cell in row) for row in grid)
 

JavaScript:

function countUnguarded(m, n, guards, walls) {
    const grid = Array.from({ length: m }, () => Array(n).fill(0));
    for (const [r, c] of guards) grid[r][c] = 2;
    for (const [r, c] of walls) grid[r][c] = 2;
    for (const [r, c] of guards) {
        for (const [dr, dc] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
            let nr = r + dr, nc = c + dc;
            while (nr >= 0 && nr < m && nc >= 0 && nc < n && grid[nr][nc] < 2) {
                grid[nr][nc] = 1;
                nr += dr;
                nc += dc;
            }
        }
    }
    return grid.flat().filter(cell => cell === 0).length;
}

The other languages (Java, C++, Go, TypeScript) would follow a similar structure, adapting the syntax to their respective languages. The core logic remains the same across all implementations. Choose the approach and language that best suits your needs and understanding. Approach 1 (Simulation) is generally easier to understand, while Approach 2 (DFS) might be slightly more efficient in specific cases but adds a layer of recursion.