Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1001. Grid Illumination - Leetcode Solution

Problem Description

You are given an n x n grid, some of whose cells contain lamps. Each lamp illuminates its entire row, column, and both diagonals (top-left to bottom-right, and top-right to bottom-left). You are also given a list of queries, where each query is a cell (row, col). For each query, you must determine whether the queried cell is illuminated by any lamp at that moment.

After checking illumination for a query, you must turn off any lamp that is in the cell queried or in any of its 8 adjacent cells (including diagonals). A lamp that is turned off no longer illuminates any cells for subsequent queries.

Return a list of integers, where each element is 1 if the corresponding query cell is illuminated, or 0 otherwise.

Constraints:

  • 1 <= n <= 10^9
  • 0 <= lamps.length <= 20000
  • 0 <= queries.length <= 20000
  • 0 <= row, col < n
  • Lamps may be placed at the same position multiple times; only the first placement counts.

Thought Process

The brute-force approach would be, for each query, to check all lamps and see if any lamp illuminates the query cell. However, with up to 20,000 lamps and 20,000 queries, and a grid size up to 109, this is too slow.

Instead, we need a way to quickly determine if any lamp is illuminating a given cell. Since a lamp illuminates its row, column, and both diagonals, we can keep track of how many lamps are currently illuminating each row, column, and diagonal, using hash maps. For each query, we can check these maps in constant time.

After each query, we need to turn off any lamp in the 3x3 area centered on the query cell. We must update our hash maps accordingly, ensuring that lamps are only removed once.

The main challenge is efficiently updating and querying the illumination status, while handling lamp removals correctly.

Solution Approach

  • Data Structures:
    • Use a set to store the unique positions of all currently active lamps, so we can check and remove lamps efficiently.
    • Use four hash maps (dictionaries) to count the number of lamps illuminating each:
      • Row (row_counts)
      • Column (col_counts)
      • Main diagonal (diag_counts, where row - col is constant)
      • Anti-diagonal (anti_diag_counts, where row + col is constant)
  • Initialization:
    • For each unique lamp position, add it to the set and increment the respective counts in each hash map.
  • Processing Queries:
    1. For each query cell (r, c):
      1. Check if row_counts[r], col_counts[c], diag_counts[r-c], or anti_diag_counts[r+c] is greater than zero. If any is, the cell is illuminated.
      2. For the 3x3 area centered at (r, c), check each cell. If there is a lamp at that cell, remove it from the set and decrement the counts in all four hash maps.
  • Efficiency:
    • All operations (lookup, add, remove) in hash maps and sets are O(1) on average.
    • Each query processes at most 9 cells for possible lamp removals.

Example Walkthrough

Input:
n = 5
lamps = [[0,0],[4,4]]
queries = [[1,1],[1,0]]

Step 1: Initialization
- Active lamps: {(0,0), (4,4)}
- row_counts: {0:1, 4:1}
- col_counts: {0:1, 4:1}
- diag_counts: {0:1, 0:1} (0-0=0, 4-4=0) => {0:2}
- anti_diag_counts: {0:1, 8:1} (0+0=0, 4+4=8)

Step 2: First Query (1,1)
- Is row_counts[1] > 0? No.
- Is col_counts[1] > 0? No.
- Is diag_counts[0] > 0? Yes (2).
- So, cell is illuminated. Output 1.
- Now, turn off any lamp in the 3x3 area centered at (1,1): check (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2). Only (0,0) is a lamp. - Remove (0,0) from active lamps, decrement counts:
row_counts[0]: 0, col_counts[0]: 0, diag_counts[0]: 1, anti_diag_counts[0]: 0

Step 3: Second Query (1,0)
- Is row_counts[1] > 0? No.
- Is col_counts[0] > 0? No.
- Is diag_counts[1-0=1] > 0? No.
- Is anti_diag_counts[1+0=1] > 0? No.
- So, cell is not illuminated. Output 0.
- Turn off lamps in the 3x3 area centered at (1,0): (0,-1), (0,0), (0,1), (1,-1), (1,0), (1,1), (2,-1), (2,0), (2,1). Only (0,0) has already been removed, so nothing changes.

Final Output: [1, 0]

Time and Space Complexity

Brute-force Approach:
- For each query, check all lamps: O(Q * L), where Q is the number of queries and L is the number of lamps. This is too slow for large inputs.

Optimized Approach:
- Initialization: O(L), for processing all lamps.
- For each query:

  • Checking illumination: O(1), using hash maps.
  • Turning off lamps: O(1) per cell, up to 9 cells per query (since a lamp can only be removed once).
- Total time: O(L + Q), since each lamp is only removed once and each query is processed in constant time.
- Space: O(L), for storing lamp positions and counts.

Summary

The Grid Illumination problem is efficiently solved by using hash maps to keep track of how many lamps illuminate each row, column, and diagonal. By using a set to store lamp positions, we can efficiently remove lamps and update our counts. This approach allows us to answer each query in constant time and handle lamp removals efficiently, making the solution scalable to the large input sizes specified by the problem.

Code Implementation

from collections import defaultdict

class Solution:
    def gridIllumination(self, n, lamps, queries):
        row = defaultdict(int)
        col = defaultdict(int)
        diag = defaultdict(int)
        anti_diag = defaultdict(int)
        lamp_set = set()
        
        # Add unique lamps and count
        for x, y in lamps:
            if (x, y) in lamp_set:
                continue
            lamp_set.add((x, y))
            row[x] += 1
            col[y] += 1
            diag[x - y] += 1
            anti_diag[x + y] += 1
        
        res = []
        for x, y in queries:
            if row[x] or col[y] or diag[x - y] or anti_diag[x + y]:
                res.append(1)
            else:
                res.append(0)
            # Turn off lamps in 3x3 area
            for dx in [-1, 0, 1]:
                for dy in [-1, 0, 1]:
                    nx, ny = x + dx, y + dy
                    if (nx, ny) in lamp_set:
                        lamp_set.remove((nx, ny))
                        row[nx] -= 1
                        col[ny] -= 1
                        diag[nx - ny] -= 1
                        anti_diag[nx + ny] -= 1
        return res
      
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;

class Solution {
public:
    vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
        unordered_map<int, int> row, col, diag, anti_diag;
        unordered_set<long long> lamp_set;
        for (auto &lamp : lamps) {
            int x = lamp[0], y = lamp[1];
            long long key = ((long long)x << 32) | y;
            if (lamp_set.count(key)) continue;
            lamp_set.insert(key);
            row[x]++;
            col[y]++;
            diag[x - y]++;
            anti_diag[x + y]++;
        }
        vector<int> res;
        vector<pair<int, int>> dirs = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,0}, {0,1}, {1,-1}, {1,0}, {1,1}};
        for (auto &q : queries) {
            int x = q[0], y = q[1];
            if (row[x] || col[y] || diag[x - y] || anti_diag[x + y]) {
                res.push_back(1);
            } else {
                res.push_back(0);
            }
            // Turn off lamps in 3x3 area
            for (auto &d : dirs) {
                int nx = x + d.first, ny = y + d.second;
                long long key = ((long long)nx << 32) | ny;
                if (lamp_set.count(key)) {
                    lamp_set.erase(key);
                    row[nx]--;
                    col[ny]--;
                    diag[nx - ny]--;
                    anti_diag[nx + ny]--;
                }
            }
        }
        return res;
    }
};
      
import java.util.*;

class Solution {
    public int[] gridIllumination(int n, int[][] lamps, int[][] queries) {
        Map<Integer, Integer> row = new HashMap<>();
        Map<Integer, Integer> col = new HashMap<>();
        Map<Integer, Integer> diag = new HashMap<>();
        Map<Integer, Integer> antiDiag = new HashMap<>();
        Set<Long> lampSet = new HashSet<>();
        for (int[] lamp : lamps) {
            int x = lamp[0], y = lamp[1];
            long key = ((long)x << 32) | y;
            if (lampSet.contains(key)) continue;
            lampSet.add(key);
            row.put(x, row.getOrDefault(x, 0) + 1);
            col.put(y, col.getOrDefault(y, 0) + 1);
            diag.put(x - y, diag.getOrDefault(x - y, 0) + 1);
            antiDiag.put(x + y, antiDiag.getOrDefault(x + y, 0) + 1);
        }
        int[] res = new int[queries.length];
        int[][] dirs = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,0}, {0,1}, {1,-1}, {1,0}, {1,1}};
        for (int i = 0; i < queries.length; i++) {
            int x = queries[i][0], y = queries[i][1];
            if (row.getOrDefault(x, 0) > 0 ||
                col.getOrDefault(y, 0) > 0 ||
                diag.getOrDefault(x - y, 0) > 0 ||
                antiDiag.getOrDefault(x + y, 0) > 0) {
                res[i] = 1;
            } else {
                res[i] = 0;
            }
            for (int[] d : dirs) {
                int nx = x + d[0], ny = y + d[1];
                long key = ((long)nx << 32) | ny;
                if (lampSet.contains(key)) {
                    lampSet.remove(key);
                    row.put(nx, row.get(nx) - 1);
                    col.put(ny, col.get(ny) - 1);
                    diag.put(nx - ny, diag.get(nx - ny) - 1);
                    antiDiag.put(nx + ny, antiDiag.get(nx + ny) - 1);
                }
            }
        }
        return res;
    }
}
      
var gridIllumination = function(n, lamps, queries) {
    const row = new Map(), col = new Map(), diag = new Map(), antiDiag = new Map();
    const lampSet = new Set();
    for (const [x, y] of lamps) {
        const key = x + ',' + y;
        if (lampSet.has(key)) continue;
        lampSet.add(key);
        row.set(x, (row.get(x) || 0) + 1);
        col.set(y, (col.get(y) || 0) + 1);
        diag.set(x - y, (diag.get(x - y) || 0) + 1);
        antiDiag.set(x + y, (antiDiag.get(x + y) || 0) + 1);
    }
    const res = [];
    const dirs = [[-1,-1], [-1,0], [-1,1], [0,-1], [0,0], [0,1], [1,-1], [1,0], [1,1]];
    for (const [x, y] of queries) {
        if ((row.get(x) || 0) > 0 ||
            (col.get(y) || 0) > 0 ||
            (diag.get(x - y) || 0) > 0 ||
            (antiDiag.get(x + y) || 0) > 0) {
            res.push(1);
        } else {
            res.push(0);
        }
        for (const [dx, dy] of dirs) {
            const nx = x + dx, ny = y + dy;
            const key = nx + ',' + ny;
            if (lampSet.has(key)) {
                lampSet.delete(key);
                row.set(nx, row.get(nx) - 1);
                col.set(ny, col.get(ny) - 1);
                diag.set(nx - ny, diag.get(nx - ny) - 1);
                antiDiag.set(nx + ny, antiDiag.get(nx + ny) - 1);
            }
        }
    }
    return res;
};