Count Unguarded Cells in the Grid

Updated on 20 May, 2025
Count Unguarded Cells in the Grid header image

Problem Statement

In the given problem, we need to evaluate a scenario taking place on an m x n grid, which is a 0-indexed matrix layout. This grid may contain guards and walls placed at specified positions. The guards have the ability to view every direction: north, east, south, and west from their position. However, their line of sight is obstructed by either another guard or a wall which blocks their view.

A cell on this grid is deemed 'guarded' if at least one guard can view it without obstructions. Our goal in this problem is to determine the number of cells within the grid that are neither occupied by guards nor walls and are also not visible by any of the guards, thereby being completely unguarded.

Examples

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.

Approach and Intuition

To solve this problem, the strategy involves simulating the view of each guard and marking the cells they can see as guarded. Follow these steps:

  1. Initialize a matrix representation of the grid with every cell marked as unguarded.
  2. Mark cells containing guards and walls as they are already known and cannot be considered unprotected.
  3. For each guard, propagate their view in all four cardinal directions:
    • North - decrement row index until the top or an obstruction is encountered.
    • South - increment row index until the bottom or an obstruction is encountered.
    • East - increment column index until the rightmost column or an obstruction is encountered.
    • West - decrement column index until the leftmost column or an obstruction is encountered.
  4. During the propagation, change the status of each uncovered cell to guarded.
  5. Once all guards have been assessed, count and return the total number of cells that remain unmarked by any guard and are not positions of guards or walls.

The examples use a visualization method to explain how the guards cover certain cells until thwarted by a wall or the grid's boundary, demonstrating the result of calculating the unprotected areas left in the grid after applying the guards' coverages. Following this intuitive, step-wise method ensures every unguarded cell on the grid is counted once, capturing the essence of the problem through systematic checking and visualization of the guard's potential field of coverage.

Solutions

  • C++
  • Java
  • Python
cpp
class GuardManager {
public:
    const int UNPROTECTED = 0;
    const int PROTECTED = 1;
    const int GUARD_POSITION = 2;
    const int OBSTACLE = 3;

    int getUnprotectedCount(int m, int n, vector<vector<int>>& guards,
                            vector<vector<int>>& walls) {
        vector<vector<int>> area(m, vector<int>(n, UNPROTECTED));

        // Set guard positions in the area
        for (const auto& guard : guards) {
            area[guard[0]][guard[1]] = GUARD_POSITION;
        }

        // Set wall positions in the area
        for (const auto& wall : walls) {
            area[wall[0]][wall[1]] = OBSTACLE;
        }

        // Lambda for visibility updates
        auto handleVisibility = [&](int row, int col, bool activeGuard) -> bool {
            if (area[row][col] == GUARD_POSITION) {
                return true;
            }
            if (area[row][col] == OBSTACLE) {
                return false;
            }
            if (activeGuard) {
                area[row][col] = PROTECTED;
            }
            return activeGuard;
        };

        // Move through rows
        for (int row = 0; row < m; row++) {
            bool activeGuard = area[row][0] == GUARD_POSITION;
            for (int col = 1; col < n; col++) {
                activeGuard = handleVisibility(row, col, activeGuard);
            }

            activeGuard = area[row][n - 1] == GUARD_POSITION;
            for (int col = n - 2; col >= 0; col--) {
                activeGuard = handleVisibility(row, col, activeGuard);
            }
        }

        // Move through columns
        for (int col = 0; col < n; col++) {
            bool activeGuard = area[0][col] == GUARD_POSITION;
            for (int row = 1; row < m; row++) {
                activeGuard = handleVisibility(row, col, activeGuard);
            }

            activeGuard = area[m - 1][col] == GUARD_POSITION;
            for (int row = m - 2; row >= 0; row--) {
                activeGuard = handleVisibility(row, col, activeGuard);
            }
        }

        // Calculate unprotected cells
        int unprotectedCells = 0;
        for (int row = 0; row < m; row++) {
            for (int col = 0; col < n; col++) {
                if (area[row][col] == UNPROTECTED) {
                    unprotectedCells++;
                }
            }
        }

        return unprotectedCells;
    }
};

In the given C++ code, you track the number of unguarded cells in a grid using a class named GuardManager. The strategy involves representing the grid and iterating over it to update cell states based on the presence of guards, obstacles, and visibility from guards.

Here's how the algorithm works:

  • Initialize every cell of the grid-sized m x n as unguarded. Represent the different cell states using constants: UNPROTECTED, PROTECTED, GUARD_POSITION, and OBSTACLE.
  • Place guards and obstacles into the grid based on the input vectors guards and walls.
  • Define a lambda function, handleVisibility, to manage the visibility propagation horizontally and vertically. If a guard is active and no obstacles block its view, it marks cells as protected.
  • Iterate every row from left to right, then right to left. During each pass, update the protection state of the cells if guarded by the logic in handleVisibility.
  • Repeat similar iterations for every column from top to bottom and then bottom to top.
  • After updating the entire grid, count and return the number of unguarded (UNPROTECTED) cells.

The approach ensures every cell's protection status is updated correctly considering the positions of guards and obstacles, and how visibility is obstructed. This meticulous cell by cell update in both horizontal and vertical directions ensures no cell's protection status is overlooked, providing an accurate count of unprotected cells.

java
class GuardSystem {

    private static final int OPEN = 0;
    private static final int WATCHED = 1;
    private static final int GUARD = 2;
    private static final int OBSTACLE = 3;

    public int countOpenAreas(int m, int n, int[][] guards, int[][] obstacles) {
        // Setup grid
        int[][] area = new int[m][n];

        // Mark guards in grid
        for (int[] g : guards) {
            area[g[0]][g[1]] = GUARD;
        }

        // Mark obstacles in grid
        for (int[] o : obstacles) {
            area[o[0]][o[1]] = OBSTACLE;
        }

        // Horizontal passes
        for (int i = 0; i < m; i++) {
            // Left to right pass
            boolean isActive = area[i][0] == GUARD;
            for (int j = 1; j < n; j++) {
                isActive = updateVisibility(area, i, j, isActive);
            }

            // Right to left pass
            isActive = area[i][n - 1] == GUARD;
            for (int j = n - 2; j >= 0; j--) {
                isActive = updateVisibility(area, i, j, isActive);
            }
        }

        // Vertical passes
        for (int j = 0; j < n; j++) {
            // Top to bottom pass
            boolean isActive = area[0][j] == GUARD;
            for (int i = 1; i < m; i++) {
                isActive = updateVisibility(area, i, j, isActive);
            }

            // Bottom to top pass
            isActive = area[m - 1][j] == GUARD;
            for (int i = m - 2; i >= 0; i--) {
                isActive = updateVisibility(area, i, j, isActive);
            }
        }

        // Count Open areas
        int unwatchedCount = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (area[i][j] == OPEN) {
                    unwatchedCount++;
                }
            }
        }

        return unwatchedCount;
    }

    // Helper function to update visibility status
    private boolean updateVisibility(int[][] area, int i, int j, boolean isActive) {
        if (area[i][j] == GUARD) {
            return true;
        }
        if (area[i][j] == OBSTACLE) {
            return false;
        }
        if (isActive) {
            area[i][j] = WATCHED;
        }
        return isActive;
    }
}

This Java solution is designed to count unwatched or unguarded cells on a 2D grid, where guards and obstacles are placed. The method countOpenAreas processes the grid to determine these unwatched cells.

Here is an overview of the logic implemented in the code:

  1. Initialize a grid representation where each cell can be marked as open, watched, with a guard, or with an obstacle.
  2. Fill in the guard and obstacle positions on the grid using input arrays.
  3. Implement two passes (horizontal and vertical) for each row and column:
    • For horizontally, make left to right and right to left passes.
    • For vertically, make top to bottom and bottom to top passes.
    • During these passes, update the cell’s status to watched if it falls within the line of sight of a guard until an obstacle blocks that view.
  4. Finally, count all cells that remain open (i.e., those not watched by any guard or blocked by obstacles).

The auxiliary function, updateVisibility, helps in determining whether a particular cell in a row or column should be considered watched depending on the presence of guards and obstacles.

This approach helps in correctly calculating and maintaining the state of each cell, ensuring that the solution is both accurate and efficient for larger grids.

python
class SecuritySystem:
    SAFE = 0
    SECURE = 1
    GUARDIAN = 2
    BARRIER = 3

    def totalSafeSpaces(
        self, M: int, N: int, guardians: List[List[int]], barriers: List[List[int]]
    ) -> int:
        matrix = [[self.SAFE] * N for _ in range(M)]

        # Define positions of guardians
        for row, col in guardians:
            matrix[row][col] = self.GUARDIAN

        # Define positions of barriers
        for row, col in barriers:
            matrix[row][col] = self.BARRIER

        # Help function to adjust visibility
        def _adjustVisibility(r, c, is_guard_active):
            if matrix[r][c] == self.GUARDIAN:
                return True
            if matrix[r][c] == self.BARRIER:
                return False
            if is_guard_active:
                matrix[r][c] = self.SECURE
            return is_guard_active

        # Horizontal checks
        for r in range(M):
            guard_status = matrix[r][0] == self.GUARDIAN
            for c in range(1, N):
                guard_status = _adjustVisibility(r, c, guard_status)
            guard_status = matrix[r][N - 1] == self.GUARDIAN
            for c in range(N - 2, -1, -1):
                guard_status = _adjustVisibility(r, c, guard_status)

        # Vertical checks
        for c in range(N):
            guard_status = matrix[0][c] == self.GUARDIAN
            for r in range(1, M):
                guard_status = _adjustVisibility(r, c, guard_status)
            guard_status = matrix[M - 1][c] == self.GUARDIAN
            for r in range(M - 2, -1, -1):
                guard_status = _adjustVisibility(r, c, guard_status)

        # Count safe zones
        safe_count = 0
        for r in range(M):
            for c in range(N):
                if matrix[r][c] == self.SAFE:
                    safe_count += 1
        return safe_count

The Python solution presented calculates the number of unguarded cells in a grid using a security system that determines the visibility from guardians (guards) and the presence of barriers. The solution involves creating and initializing a matrix to represent the grid, where cells can be safe, secure, occupied by a guardian, or blocked by a barrier.

  • Initialize the grid matrix to all cells as SAFE.
  • Place guardians and barriers on the grid based on given input positions.
  • Define a helper function to adjust the visibility in the grid. This function checks the state of the current cell (whether it is a guardian or a barrier or a secure area) and updates the visibility accordingly.
  • Perform checks row-wise (both left to right and right to left) and column-wise (both top to bottom and bottom to top) to simulate the spread of visibility from guardians, blocked by barriers.
  • Count unsecured or safe cells at the end by iterating through the grid and counting cells that remain in the SAFE state.

By following these steps, the program efficiently calculates the number of cells in the grid that are not visible from any guardians. The performance is ensured by direct grid manipulation, and the use of helper functions keeps the code organized and readable.

Comments

No comments yet.