有一个 m x n 的二元网格,其中 1 表示砖块,0 表示空白。砖块 稳定(不会掉落)的前提是:
一块砖直接连接到网格的顶部,或者
至少有一块相邻(4 个方向之一)砖块 稳定 不会掉落时
给你一个数组 hits ,这是需要依次消除砖块的位置。每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而掉落。一旦砖块掉落,它会立即从网格中消失(即,它不会落在其他稳定的砖块上)。
返回一个数组 result ,其中 result[i] 表示第 i 次消除操作对应掉落的砖块数目。
注意,消除可能指向是没有砖块的空白位置,如果发生这种情况,则没有砖块掉落。
示例 1:
输入:grid = [[1,0,0,0],[1,1,1,0]], hits = [[1,0]]
输出:[2]
解释:
网格开始为:
[[1,0,0,0],
[1,1,1,0]]
消除 (1,0) 处加粗的砖块,得到网格:
[[1,0,0,0]
[0,1,1,0]]
两个加粗的砖不再稳定,因为它们不再与顶部相连,也不再与另一个稳定的砖相邻,因此它们将掉落。得到网格:
[[1,0,0,0],
[0,0,0,0]]
因此,结果为 [2] 。
示例 2:
输入:grid = [[1,0,0,0],[1,1,0,0]], hits = [[1,1],[1,0]]
输出:[0,0]
解释:
网格开始为:
[[1,0,0,0],
[1,1,0,0]]
消除 (1,1) 处加粗的砖块,得到网格:
[[1,0,0,0],
[1,0,0,0]]
剩下的砖都很稳定,所以不会掉落。网格保持不变:
[[1,0,0,0],
[1,0,0,0]]
接下来消除 (1,0) 处加粗的砖块,得到网格:
[[1,0,0,0],
[0,0,0,0]]
剩下的砖块仍然是稳定的,所以不会有砖块掉落。
因此,结果为 [0,0] 。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
grid[i][j] 为 0 或 1
1 <= hits.length <= 4 * 104
hits[i].length == 2
0 <= xi <= m - 1
0 <= yi <= n - 1
所有 (xi, yi) 互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bricks-falling-when-hit
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
又是一个并查集的问题,解法也特别复杂,思路分为三步:
1.将所有即将敲击的位置进行减1操作
2.将现在所有与顶层有直接、间接连接的砖块恢复
3.从最后一次hit开始,逐步恢复砖块,并且找到因为hit当前砖块而掉落的砖块,将其加入remain中
这是参考大佬的详细代码,代码如下:
class Solution {
public:
vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {
set<pair<int,int>> nowRemain;//存放当前状态不掉落的砖块信息
int hitsSize = (int)hits.size();
vector<int> result(hitsSize, 0);//存储结果
int rowSize = (int)grid.size(), colSize = (int)grid[0].size();
//第一步:将所有即将敲击的位置进行减1操作,让它减一而不是变为零,是因为可能hit了多次,只有还原最开始的那一次hit之后才能将这个砖块恢复
for (int i = 0; i < hitsSize; ++i) {
--grid[hits[i][0]][hits[i][1]];
}
//第二步:将现在所有与顶层有直接、间接连接的砖块恢复(因为第一步去除了所有敲击的位置的砖块,所以当所有敲击完成后,这些与与顶层有直接、间接连接的砖块仍然不会掉落
for (int col = 0; col < colSize; ++col) {
if(grid[0][col] == 1) {//顶层第col个位置
findRemain(grid, 0, col, nowRemain);
}
}
//第三步:从最后一次hit开始,逐步恢复砖块,并且找到因为hit当前砖块而掉落的砖块,将其加入remain中
for (int k = hitsSize - 1; k >= 0; --k) {
int row = hits[k][0];//获取敲击的位置
int col = hits[k][1];
grid[row][col] += 1;//在第一步的时候是减一,这里进行加1逆操作
if(grid[row][col] == 1) {//grid[row][col] != 1,说明这个位置hit了多次,还没有到最开始的那个hit,所以不能恢复
//注意我们是从后往前逆恢复,当前remain的大小是敲击了当前位置后剩余的大小
int afterHitRemainSize = (int)nowRemain.size();//敲击当前砖块后的剩余砖块数(
//我们现在需要判断grid[row][col]是否是能够不掉落(只有当grid[row][col]在顶层,或者上下左右存在不掉落的砖块,则grid[row][col]也能够不掉落
//如果当前hit的砖块不能固定,说明在hit前它已经掉落了,那么就不应该搜索恢复它周围的砖块,因为它们不是因为hit它而掉落的
if(row == 0 || (row > 0 && nowRemain.count({row - 1, col})) || (row < rowSize - 1 && nowRemain.count({row + 1, col}))
|| (col > 0 && nowRemain.count({row, col - 1})) || (col < colSize - 1 && nowRemain.count({row, col + 1}))){
findRemain(grid, row , col, nowRemain);//开始恢复grid[row][col]及其直接、间接相连的砖块
//现在remain的大小是敲击grid[row][col]之前剩余的砖块数,而afterHitRemainSize是敲击之后剩余的砖块数
result[k] = (int)nowRemain.size() - afterHitRemainSize - 1; //grid[row][col]这个砖块是敲击掉的,不算在掉落砖块数内
}
}
}
return result;
}
//在grid[row][col]不掉落的前提下,我们将它上下左右四个方向的砖进行恢复
void findRemain(vector<vector<int>>& grid, int row, int col, set<pair<int,int>>& nowRemain) {
int rowSize = (int)grid.size(), colSize = (int)grid[0].size();
//如果这grid[row][col]不是砖块,则不是恢复的前提,h如果这个位置已经在remain中,说明这块砖已经恢复了
if(grid[row][col] != 1 || nowRemain.count({row,col})) {
return;
}
nowRemain.insert({row, col});//插入到nowRemain中(表示恢复
//分别恢复它的上下左右四个方向
if(row > 0) {
findRemain(grid, row - 1, col, nowRemain);
}
if(row < rowSize - 1) {
findRemain(grid, row + 1, col, nowRemain);
}
if(col > 0) {
findRemain(grid, row, col - 1, nowRemain);
}
if(col < colSize - 1) {
findRemain(grid, row, col + 1, nowRemain);
}
}
};