描述
有一个 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/
求解(参考leetcode官方答案:https://leetcode-cn.com/problems/bricks-falling-when-hit/solution/da-zhuan-kuai-by-leetcode-solution-szrq/)
// 使用散列表实现一个动态增加的并查集
class VarUnionFind {
private:
unordered_map<int, int> parent;
unordered_map<int, int> rank;
unordered_map<int, int> sz;
public:
int find(int p) {
if (parent.count(p) == 0) {
// 如果p在散列表中不存在,则添加进去
parent[p] = p; // 初始指向自己
rank[p] = 1; // 初始层级为1
sz[p] = 1; // 初始数量为1
return p;
}
while (p != parent[p]) {
parent[p] = parent[parent[p]]; // 路径压缩,优化查找速度
p = parent[p];
}
return p;
}
void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) {
return;
}
if (rank[pRoot] < rank[qRoot]) {
parent[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
return;
}
if (rank[pRoot] > rank[qRoot]) {
parent[qRoot] = pRoot;
sz[pRoot] += sz[qRoot];
return;
}
// rank[pRoot] == rank[qRoot]
parent[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
++rank[qRoot];
}
// 获取当前连通分量内的元素数量
int getCount(int p) {
return sz[find(p)];
}
};
class Solution {
public:
vector<int> hitBricks(vector<vector<int>> &grid, vector<vector<int>> &hits) {
const int h = grid.size();
const int w = grid[0].size();
auto status = grid;
// 将需要粉碎的砖块位置均重置为0,便于逆序处理
for (auto &vec : hits) {
status[vec[0]][vec[1]] = 0;
}
// 并查集关联
VarUnionFind uf;
for (int r = 0; r < h; ++r) {
for (int c = 0; c < w; ++c) {
if (status[r][c] == 1) {
if (r == 0) {
uf.unionElements(h * w, c); // h * w用来表示第一行的特殊点
}
if (r > 0 && status[r - 1][c] == 1) {
uf.unionElements(r * w + c, (r - 1) * w + c);
}
if (c > 0 && status[r][c - 1] == 1) {
uf.unionElements(r * w + c, r * w + c - 1);
}
}
}
}
// 计算结果
vector<std::pair<int, int>> directions{{1, 0},{0, 1},{-1, 0},{0, -1}};
const int n = hits.size();
vector<int> res(n, 0);
for (int i = n - 1; i >= 0; --i) {
int r = hits[i][0];
int c = hits[i][1];
if (grid[r][c] == 0) {
continue;
}
int prevCount = uf.getCount(h * w); // 获取当前节点添加前的连通量节点数量
if (r == 0) {
uf.unionElements(c, h * w); // h * w用来表示第一行的特殊点
}
for (auto &[x, y] : directions) {
int nr = r + x;
int nc = c + y;
if (nr >= 0 && nr < h && nc >= 0 && nc < w) {
// 与当前节点的上下左右1节点关联
if (status[nr][nc] == 1) {
uf.unionElements(nr * w + nc, r * w + c);
}
}
}
int curCount = uf.getCount(h * w);
res[i] = std::max(0, curCount - prevCount - 1);
status[r][c] = 1;
}
return res;
}
};