题目
有一个 m x n 的二元网格,其中 1 表示砖块,0 表示空白。砖块 稳定(不会掉落)的前提是:
一块砖直接连接到网格的顶部,或者
至少有一块相邻(4 个方向之一)砖块 稳定 不会掉落时
给你一个数组 hits ,这是需要依次消除砖块的位置。每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而掉落。一旦砖块掉落,它会立即从网格中消失(即,它不会落在其他稳定的砖块上)。
返回一个数组 result ,其中 result[i] 表示第 i 次消除操作对应掉落的砖块数目。
样例
输入: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] 。
并查集
消除一个砖块的效果是:一个连通分量被分成了两个连通分量;
并查集的作用是:把两个连通分量合并成一个连通分量。
提示我们这个问题需要 反向 思考。即考虑:补上被击碎的砖块以后,有多少个砖块因为这个补上的这个砖块而与屋顶的砖块相连。每一次击碎一个砖块,因击碎砖块而消失的砖块只会越来越少。因此可以按照数组 hits 的顺序 逆序地 把这些砖块依次补上。如图所示:
当最后一块砖块补上的时候,就恰好可以恢复成刚开始的时候整个二维表格的样子。
作者:LeetCode
链接:https://leetcode-cn.com/problems/bricks-falling-when-hit/solution/803-da-zhuan-kuai-by-leetcode-r5kf/
class Solution {
int cols;
int rows;
private final int[][] DIRECTIONS = {{0,1},{1,0},{-1,0},{0,-1}};
public int[] hitBricks(int[][] grid, int[][] hits) {
this.cols = grid[0].length;
this.rows = grid.length;
int[][] copy = new int[rows][cols];
int size = rows * cols;
UnionFind un = new UnionFind(size + 1);
// 原数组copy
for(int i = 0;i < rows;i++){
for(int j = 0;j < cols;j++){
copy[i][j] = grid[i][j];
}
}
for(int[] hit : hits){
int x = hit[0];
int y = hit[1];
copy[x][y] = 0;
}
for(int i = 0; i < cols; i++){
if(copy[0][i] == 1){
un.union(i,size);
}
}
for(int i = 1; i < rows; i++){
for(int j = 0; j < cols; j++){
if(copy[i][j] == 1){
// 保证上方也是方块
if(copy[i - 1][j] == 1){
un.union(getIndex(i,j),getIndex(i - 1,j));
}
// 如果左边是方块
if(j > 0 && copy[i][j - 1] == 1){
un.union(getIndex(i,j),getIndex(i,j - 1));
}
}
}
}
int[] res = new int[hits.length];
for(int i = hits.length - 1; i >= 0; i--){
int x = hits[i][0];
int y = hits[i][1];
if(grid[x][y] == 0){
continue;
}
// 补回之前的
int origin = un.getSize(size);
// 如果是顶层
if(x == 0){
un.union(y,size);
}
for(int[] direction : DIRECTIONS){
int newX = x + direction[0];
int newY = y + direction[1];
if(inGraph(newX,newY) && copy[newX][newY] == 1){
un.union(getIndex(newX,newY),getIndex(x,y));
}
}
// 得到现在的
int current = un.getSize(size);
res[i] = Math.max(0,current - origin - 1);
// 补上砖块
copy[x][y] = 1;
}
return res;
}
// 判断是否在图中
public boolean inGraph(int i,int j){
if(i < 0 || j < 0 || i >= rows || j >= cols){
return false;
}
return true;
}
// 得到二维数组转为一维数组位置
public int getIndex(int i,int j){
return i * cols + j;
}
private class UnionFind{
int size;
int f[];
int rank[];
public UnionFind(int size){
this.size = size;
f = new int[size];
rank = new int[size];
Arrays.fill(rank,1);
for(int i = 0;i < size;i++){
f[i] = i;
}
}
public int find(int x){
return f[x] = f[x] == x ? x : find(f[x]);
}
public void union(int u,int v){
u = find(u);
v = find(v);
if(u == v){
return;
}
if(rank[u] < rank[v]){
int temp = u;
u = v;
v = temp;
}
rank[u] += rank[v];
f[v] = u;
}
public int getSize(int x){
x = find(x);
return rank[x];
}
}
}