LeetCode-803. BrickBreaker

description

There is a binary grid of mxn, where 1 means brick and 0 means blank. The premise that the bricks are stable (will not fall) are:

When a brick is directly connected to the top of the grid, or
at least one adjacent brick (one of the 4 directions) is stable and will not fall,
you will be given an array of hits, which is where the bricks need to be eliminated in turn. Whenever the brick at the position of hits[i] = (rowi, coli) is eliminated, the brick at the corresponding position (if any) will disappear, and then other bricks may fall due to this elimination operation. Once a brick falls, it will immediately disappear from the grid (ie, it will not fall on other stable bricks).

Return an array result, where result[i] represents the number of bricks dropped corresponding to the i-th elimination operation.

Note that the elimination may point to a blank position without bricks. If this happens, no bricks will fall.

 

Example 1:

Input: grid = [[1,0,0,0],[1,1,1,0]], hits = [[1,0]]
Output: [2]
Explanation: The
grid starts as:
[[1 ,0,0,0],
 [1,1,1,0]]
eliminate the thickened bricks at (1,0) to get the grid:
[[1,0,0,0]
 [0,1, 1,0]] The
two bold bricks are no longer stable because they are no longer connected to the top and no longer adjacent to another stable brick, so they will fall. Get the grid:
[[1,0,0,0],
 [0,0,0,0]]
Therefore, the result is [2].
Example 2:

Input: grid = [[1,0,0,0],[1,1,0,0]], hits = [[1,1],[1,0]]
Output: [0,0]
Explanation:
The grid starts as:
[[1,0,0,0],
 [1,1,0,0]]
Eliminate the thickened bricks at (1,1) to get the grid:
[[1,0,0 ,0],
 [1,0,0,0]] The
remaining bricks are very stable, so they will not fall. The grid remains unchanged:
[[1,0,0,0], 
 [1,0,0,0]]
Next, eliminate the thickened bricks at (1,0) to get the grid:
[[1, 0,0,0],
 [0,0,0,0]] The
remaining bricks are still stable, so no bricks will fall.
Therefore, the result is [0,0].
 

prompt:

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) 互不相同

Source: LeetCode
Link: https://leetcode-cn.com/problems/bricks-falling-when-hit/

 

Solution (refer to the official answer of 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;
        }
    };

 

Guess you like

Origin blog.csdn.net/u010323563/article/details/112718142