链接:https://leetcode-cn.com/problems/bricks-falling-when-hit/
有一个 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)
互不相同。
思路:题意为所有处在顶层的砖块都是稳定的,所有与稳定砖块处于同一集合的砖块也是稳定的。这很显然是要我们维护集合的关系,需要用并查集解决。但是并查集的操作是将两个不同的集合合并,而本题是从集合中去掉一些元素,与正常的并查集操作相反。如果我们逆序考虑消除,那么就可以把逆序的消除看成是正向的增加。为了方便讨论,我们将稳定砖块处于的集合称为稳定集合,将没有稳定砖块的集合称为不稳定集合:
如上图,如果我们此时删除C,那么D、E由于不再与稳定砖块A处于一个集合而变得不稳定,也就是说因为删除了C,最终C、D、E都掉落。现在我们来看看刚刚说的逆序过程:
此时我们加入砖块C,那么C与一个稳定的集合A-B相连,其大小为2,C也与一个不稳定集合D-E相连,大小为2,集合合并后,变为:
此时集合大小变为5,加入砖块C之前只有2个稳定砖块,因此其中有3个不稳定元素,也就是删除C后,会消除3个砖块。[注]:题目要求的是因为某一次消除导致掉落的砖块数量,因此本次的掉落数量为2,而不能包括我们消除的C。我们用originalNum表示补C前的稳定砖块数量,newNum表示补C后的稳定砖块数量,那么掉落的砖块数量为newNum - originalNum - 1。
特殊情况:如果我们加入C之后,并不能导致某一个集合与稳定集合相连,也就是说稳定砖块的数量不发生变化,此时newNum - originalNum - 1 = -1显然不合题意。这种情况会发生在,我们的消除操作选在那些已经掉落的砖块上(因为题目中指明消除的砖块保证不重复,因此这种情况不可能是由于一个地方被重复消除多次导致的)。
如图,在消除了C之后,D、E会掉落,此时如果我们选择消除D,那么稳定的砖块个数是不会变化的。
我们根据我们的反向思维考虑,也就是在C没接入稳定集合的时候,我们选择加入砖块D:
这时候稳定砖块的数量并未受到任何影响,也自然不会有任何的砖块掉落:由于稳定砖块数量并未增加,因此在逆向考虑的时候就是,稳定砖块的数量并未减少,也就是并未有砖块掉落,因此我们需要更新公式为:max(0,newNum - originalNum - 1)。
为了便于讨论问题,我们抽象出一个节点N,表示稳定,N所在的集合一定是稳定集合,且该集合的大小就是目前网格中的砖块数。
class Solution {
public:
int Father[40010], Size[40010];
int Find(int x){
return x == Father[x] ? x : Father[x] = Find(Father[x]);
}
void Union(int A,int B){
A = Find(A);
B = Find(B);
if( A != B ){
Father[A] = B;
Size[B] += Size[A];
}
}
int LocToNum(int x,int y,int n){ //坐标 数字化
return x * n + y;
}
vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {
int m = grid.size(), n = grid[0].size(), i, j, N = n * m, size = hits.size(), x, y;
// 1. 拷贝原网格数组
vector<vector<int> > net;
for(i = 0; i < m; ++ i){
vector<int> col;
for(j = 0; j < n; ++ j){
col.push_back(grid[i][j]);
}
net.push_back(col);
}
// 2. 将待敲掉的砖块敲掉
for(i = 0; i < size; ++ i){
x = hits[i][0];
y = hits[i][1];
net[x][y] = 0;
}
// 3. 初始化并查集
for(i = 0; i <= N; ++ i){
Father[i] = i;
Size[i] = 1;
} // 将顶层所有的砖块设置为稳定的
for(j = 0; j < n; ++ j){
if(net[0][j] == 1){
Union(j, N);
}
}// 顶层之外的点进行并查集的初始化
for(i = 1; i < m; ++ i){
for(j = 0; j < n; ++ j){
if(net[i][j] == 1){
if(net[i - 1][j] == 1){ // 上
Union(LocToNum(i, j, n), LocToNum(i - 1, j, n));
}
if(i + 1 < m && net[i + 1][j] == 1){ // 下
Union(LocToNum(i, j, n), LocToNum(i + 1, j, n));
}
if(j > 0 && net[i][j - 1] == 1){
Union(LocToNum(i, j, n), LocToNum(i, j - 1, n));
}
if(j + 1 < n && net[i][j + 1] == 1){
Union(LocToNum(i, j, n), LocToNum(i, j + 1, n));
}
}
}
}
vector<int> ans;
int originNum, newNum;
// 4. 反向加边 统计数量
for(i = size - 1; i >= 0; -- i){
originNum = Size[Find(N)];
x = hits[i][0];
y = hits[i][1];
if(grid[x][y] == 0){ //删除的块原本就不存在
ans.push_back(0);
}else{
net[x][y] = 1;
if(x == 0){ // 如果删除的块是顶层块
Union(y, N);
}
else{ // 删除的不是顶层块 正常考虑上部
if(x > 0 && net[x - 1][y] == 1){ // 上
Union(LocToNum(x, y, n), LocToNum(x - 1, y, n));
}
}
if(x + 1 < m && net[x + 1][y] == 1){ // 下
Union(LocToNum(x, y, n), LocToNum(x + 1, y, n));
}
if(y > 0 && net[x][y - 1] == 1){
Union(LocToNum(x, y, n), LocToNum(x, y - 1, n));
}
if(y + 1 < n && net[x][y + 1] == 1){
Union(LocToNum(x, y, n), LocToNum(x, y + 1, n));
}
newNum = Size[Find(N)];
ans.push_back(max(0,newNum - originNum - 1)); // 当前加入的砖块是不能算的
}
}
reverse(ans.begin(), ans.end());
return ans;
}
};