803. 打砖块(打砖块)

2022/5/6 6:14:11

本文主要是介绍803. 打砖块(打砖块),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

803. 打砖块

有一个 m x n 的二元网格 grid ,其中 1 表示砖块,0 表示空白。砖块 稳定(不会掉落)的前提是:

  • 一块砖直接连接到网格的顶部,或者
  • 至少有一块相邻(4 个方向之一)砖块 稳定 不会掉落时

给你一个数组 hits ,这是需要依次消除砖块的位置。每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而 掉落 。一旦砖块掉落,它会 立即 从网格 grid 中消失(即,它不会落在其他稳定的砖块上)。

返回一个数组 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) 互不相同
  1 class UnionFindSet {
  2 public:
  3     void init(int n) {
  4         parent.resize(n);
  5         size.resize(n);
  6         rank.resize(n);
  7         for (int i = 0; i < n; i++) {
  8             parent[i] = i;
  9             size[i] = 1;
 10             rank[i] = 1;
 11         }
 12     }
 13     // 递归求解x的根节点
 14     // int findRoot(int x) {
 15     //     if (x != parent[x]) {
 16     //         parent[x] = findRoot(parent[x]);
 17     //     }
 18     //     return parent[x];
 19     // }
 20     // 迭代求解x的根节点
 21     int findRoot(int x) {
 22         while (x != parent[x]) {
 23             x = parent[x];
 24         }
 25         return x;
 26     }
 27     bool isConnected(int x, int y) {
 28         return (findRoot(x) == findRoot(y));
 29     }
 30     void unify(int x, int y) {
 31         if (isConnected(x, y)) {
 32             return;
 33         }
 34         int xRoot = findRoot(x);
 35         int yRoot = findRoot(y);
 36         if (rank[xRoot] < rank[yRoot]) {
 37             parent[xRoot] = yRoot;
 38             size[yRoot] += size[xRoot];
 39             rank[yRoot] += 1;
 40         } else {
 41             parent[yRoot] = xRoot;
 42             size[xRoot] += size[yRoot];
 43             rank[xRoot] += 1;
 44         }
 45         return;
 46     }
 47     int getConnetedCnt(int x) {
 48         return size[findRoot(x)];
 49     }
 50 private:
 51     vector<int> parent;
 52     vector<int> size;
 53     vector<int> rank;
 54 };
 55 class Solution : public UnionFindSet{
 56 public:
 57     vector<vector<int>> g_direction = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
 58     /*
 59     *   逆向思考:将问题打碎砖块会掉落多少个砖块转化成补上砖块会新增多少个砖块粘到房顶通过分析上述过程,得到算法思路:
 60     *   1、复制grid到copy,为什么要复制,遍历hits的时候需要判断该位置 【本来就是0(空白)还是在第二步中置的0(被打碎的砖块)】
 61     *   2、因为需要打碎的砖块一定为1,所以如果要逆向思考,补上的砖块必须是0,所以先将hits中需要打碎的砖块在copy中置为0
 62     *   3、因为房顶永远不会掉落,所以将房顶(copy第一行)初始化为一个连通分量S,只要其他连通分量与该连通分量相连了,便会粘到房顶上,不会掉落。
 63     *   4、逆序遍历hits,将hits中的砖块重新补到copy中,同时计算每一步有多少个砖块会粘到房顶上。
 64     */
 65     bool isInArea(int x, int y, int row, int col) {
 66         return (x >= 0 && x < row && y >= 0 && y < col);
 67     }
 68     vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {
 69         int row = grid.size();
 70         int col = grid[0].size();
 71         int l = hits.size();
 72         vector<int> ans(l);
 73         // 1、复制grid到copy
 74         vector<vector<int>> copy(grid);
 75         // 2、将hits中需要打碎的砖块在copy中置为0
 76         for (auto &hit : hits) {
 77             copy[hit[0]][hit[1]] = 0;
 78         }
 79         // 3、将房顶(copy第一行)初始化为一个连通分量S
 80         init(row * col + 1);
 81         for (int j = 0; j < col; j++) {
 82             if (copy[0][j] == 0) {
 83                 continue;
 84             }
 85             unify(row * col, j); // 虚拟出一个房屋根节点row * col,与之相连的砖块都不会掉落
 86         }
 87         // 初始化并查集
 88         for (int i = 1; i < row; i++) {
 89             for (int j = 0; j < col; j++) {
 90                 if (copy[i][j] == 0) {
 91                     continue;
 92                 }
 93                 // 上方
 94                 if (copy[i - 1][j] == 1) {
 95                     unify(i * col + j, (i - 1) * col + j);
 96                 }
 97                 if (j > 0 && copy[i][j - 1] == 1) {
 98                     unify(i * col + j, i * col + j - 1);
 99                 }
100             }
101         }
102         // 4、逆序遍历hits进行补砖
103         for (int k = l - 1; k >= 0; k--) {
104             int i = hits[k][0];
105             int j = hits[k][1];
106             // 原来就是空的(不是需要补的)
107             if (grid[i][j] == 0) {
108                 continue;
109             }
110             int origin = getConnetedCnt(row * col); // 虚拟根节点原始的连通分量
111             // 被补回的转块位于屋顶
112             if (i == 0) {
113                 unify(row * col, j);
114             }
115             for (auto &pos : g_direction) {
116                 int nextRow = i + pos[0];
117                 int nextCol = j + pos[1];
118                 if (!isInArea(nextRow, nextCol, row, col) || copy[nextRow][nextCol] == 0) {
119                     continue;
120                 }
121                 unify(i * col + j, nextRow * col + nextCol);
122             }
123             int curConnectedCnt = getConnetedCnt(row * col);
124             ans[k] = max(0, curConnectedCnt - origin - 1);
125             copy[i][j] = 1;
126         }
127         return ans;
128     }
129 };

 



这篇关于803. 打砖块(打砖块)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程