LeetCode 1001. Grid Illumination

原题

On a N x N grid of cells, each cell (x, y) with 0 <= x < N and 0 <= y < N has a lamp.

Initially, some number of lamps are on. lamps[i] tells us the location of the i-th lamp that is on. Each lamp that is on illuminates every square on its x-axis, y-axis, and both diagonals (similar to a Queen in chess).

For the i-th query queries[i] = (x, y), the answer to the query is 1 if the cell (x, y) is illuminated, else 0.

After each query (x, y) [in the order given by queries], we turn off any lamps that are at cell (x, y) or are adjacent 8-directionally (ie., share a corner or edge with cell (x, y).)

Return an array of answers. Each value answer[i] should be equal to the answer of the i-th query queries[i].

Example 1:

Input: N = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]]
Output: [1,0]
Explanation: 
Before performing the first query we have both lamps [0,0] and [4,4] on.
The grid representing which cells are lit looks like this, where [0,0] is the top left corner, and [4,4] is the bottom right corner:
1 1 1 1 1
1 1 0 0 1
1 0 1 0 1
1 0 0 1 1
1 1 1 1 1
Then the query at [1, 1] returns 1 because the cell is lit.  After this query, the lamp at [0, 0] turns off, and the grid now looks like this:
1 0 0 0 1
0 1 0 0 1
0 0 1 0 1
0 0 0 1 1
1 1 1 1 1
Before performing the second query we have only the lamp [4,4] on.  Now the query at [1,0] returns 0, because the cell is no longer lit.

Note:

1 <= N <= 10^9
0 <= lamps.length <= 20000
0 <= queries.length <= 20000
lamps[i].length == queries[i].length == 2

分析

给定N的范围从1到10^9,如果直接拍脑门构建NxN大的方阵来对所有数据进行存储有点不太现实,再者,构建好NxN方阵之后,其存着的太多的0并没有太多的意义。因此我们需要构造合适的数据结构对题目中提到的关系进行存储。

关系分析

  1. 灯以及其照亮的点,这个关系是双向的,因为我们需要
    • 根据照亮的点判断关哪个灯
    • 关灯后熄灭其被照亮的点
  2. 查询的位置与被灯照亮的位置,需要被用来进行查找,因此这里的关系是单向
  3. 查询的位置周围8个格与灯的位置,因为在进行一次查询之后,如果周围有灯泡,需要将灯泡熄灭,这个关系也是一个单向的关系
  4. 最后,由于一个位置可能被多个灯照亮,因此被照亮的位置与灯存在一对多的关系

数据表示及算法流程

数据表示

在对涉及得到的位置信息进行分析之后,我们可以构造相应的数据结构来对以上的数据建立联系。

  1. map<pair<int, int>, int > point_frep, 用map来建立从被照亮的点与灯的关系,**pair<int, int>**是被照亮的位置,后面的int 是该位置上的灯泡数。
  2. map<int, int>x_frep, 表示x行被照亮,第二个int表示被几个灯光照亮
  3. map<int, int>y_frep,表示y列被照亮,第二个int表示被几个灯光照亮
  4. map<int, int>sum_frep, map<int, int> diff_frep分别表示斜着的x + y,x-y行被照亮,第二个int表示被几个灯光照亮。

关于第四个关系可以由以下的图简短说明:

当灯的位置处于(0,2)时可以发现

1. 被照亮的绿色位置的值均满足(x+y = 2)
2. 被照亮的红色位置的值均满足(x- y = -2)
3. 

因此,可以很方便的根据给出点的坐标来判断是否被某灯照亮。(其实是分别处于x+y=a, x-y=b直线上)
LeetCode 1001. Grid Illumination

流程

  1. 先对lamps进行遍历,构造提到的灯与被照亮位置的关系
    1. 记录每个位置灯的数量
    2. 更新其照亮的位置
  2. 遍历queries
    1. 根据其坐标判断是否被照亮
    2. 周围8个格子是否存在灯泡,根据point_frep来判断其值,如果存在灯泡
      • “熄灭”被照亮的行、列
      • 该位置灯泡数减一

他山之石,可以攻玉(别人的答案)

在提交之后可以看到别人提交的答案,看完后再看看自己的代码便更觉得羞涩难挡,因此去掉了贴自己代码这一步,转而分析别人代码。
作者: @neal_wu

class Solution {
public:
	vector<int> gridIllumination(int N, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
		int L = lamps.size(), Q = queries.size();
		map<pair<int, int>, int> point_frep;
		map<int, int> x_frep, y_frep, sum_frep, diff_frep;

		for (auto lamp : lamps) {
			int x = lamp[0], y = lamp[1];
			point_frep[make_pair(x, y)]++;
			x_frep[x] ++;
			y_frep[y] ++;
			sum_frep[x + y] ++;
			diff_frep[x - y] ++;
		}
		vector<int> res;
		for (auto query : queries) {
			int x = query[0], y = query[1];
			bool ans = x_frep[x] > 0 || y_frep[y] > 0 || sum_frep[x + y] > 0 || diff_frep[x - y] > 0;
			res.push_back(ans ? 1 : 0);

			for (int dx = -1; dx <= 1; ++dx) {
				for (int dy = -1; dy <= 1; ++dy) {
					int nx = x + dx, ny = y + dy;
					int freq = point_frep[make_pair(nx, ny)];

					if (freq > 0) {
						x_frep[nx] -= freq;
						y_frep[ny] -= freq;
						sum_frep[nx + ny] -= freq;
						diff_frep[nx - ny] -= freq;
						point_frep[make_pair(nx, ny)] -= freq;
					}
				}
			}
		}
		return res;
	}
};
上一篇:1001. 网格照明


下一篇:C. Nice Garland Codeforces Round #535 (Div. 3) 思维题