package com.heu.wsq.leetcode.bingchaji;
/**
* 803. 打砖块
* @author wsq
* @date 2021/1/16
* 有一个 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] 。
*
* 链接:https://leetcode-cn.com/problems/bricks-falling-when-hit
*/
public class HitBricks {
private int rows;
private int cols;
public static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
public int[] hitBricks(int[][] grid, int[][] hits){
this.rows = grid.length;
this.cols = grid[0].length;
// 1.将grid中的砖头对应hits的位置全部击碎
// 通常我们不要去修改原输入的数据,因此新建一个备份数组
int[][] copy = new int[rows][cols];
for (int i = 0; i < this.rows; i++){
for (int j = 0; j < this.cols; j++){
copy[i][j] = grid[i][j];
}
}
for (int[] hit : hits) {
copy[hit[0]][hit[1]] = 0;
}
// 2.建图,将砖块之间的连接关系输入并查集
// size表示并查集的大小,同时也用于表示虚拟的屋顶结点
int size = this.rows * this.cols;
UnionFind unionFind = new UnionFind(size + 1);
//将下标为0的行砖块与屋顶相连
for (int i = 0; i < this.cols; i++){
if (copy[0][i] == 1){
unionFind.union(i, size);
}
}
// 其余网络,如果是砖块,则向上、向左看一下,如果也是砖块在并查集进行合并
for (int i = 1; i < this.rows; i++){
for (int j = 0; j < this.cols; j++){
if (copy[i][j] == 1){
if (copy[i-1][j] == 1){
unionFind.union(getIndex(i-1, j), getIndex(i, j));
}
if (j > 0 && copy[i][j - 1] == 1){
unionFind.union(getIndex(i, j - 1), getIndex(i, j));
}
}
}
}
// 3. 按照hits的逆序,在copy中补回砖块,并记录每次补回砖块导致与屋顶相连砖块数量的变化。
int hitLen = hits.length;
int[] res = new int[hitLen];
for (int i = hitLen - 1; i >=0; i--){
int x = hits[i][0];
int y = hits[i][1];
// 若原有位置为空白,直接跳过
if (grid[x][y] == 0){
continue;
}
int origin = unionFind.getSize(size);
if (x == 0) {
unionFind.union(y, size);
}
for (int[] direction : DIRECTIONS) {
int newX = x + direction[0];
int newY = y + direction[1];
if (isArea(newX, newY) && copy[newX][newY] == 1){
unionFind.union(getIndex(newX, newY), getIndex(x, y));
}
}
int current = unionFind.getSize(size);
res[i] = Math.max(0, current - origin - 1);
copy[x][y] = 1;
}
return res;
}
private boolean isArea(int newX, int newY) {
return newX >= 0 && newX < this.rows && newY >= 0 && newY < this.cols;
}
private int getIndex(int i, int j) {
return i * this.cols + j;
}
private class UnionFind{
private int[] parent;
private int[] size;
public UnionFind(int n){
this.parent = new int[n];
this.size = new int[n];
for (int i = 0; i < n; i++){
this.parent[i] = i;
this.size[i] = 1;
}
}
public int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return;
}
parent[rootX] = rootY;
// 在合并的时候维护数组 size
size[rootY] += size[rootX];
}
public int getSize(int x){
int root = find(x);
return this.size[root];
}
}
}
803. 打砖块(并查集,要学会建立连通关系)
最新推荐文章于 2025-03-16 19:56:17 发布
本文介绍如何使用并查集数据结构解决LeetCode题目803 - 打砖块问题,通过模拟砖块的连接关系来计算每次击碎后掉落的砖块数。核心步骤包括击碎砖块、建立并查集模型、跟踪并更新砖块稳定性。
823

被折叠的 条评论
为什么被折叠?



