Leetcode 803. 打砖块 C++

Leetcode 803. 打砖块

题目

我们有一组包含1和0的网格;其中1表示砖块。 当且仅当一块砖直接连接到网格的顶部,或者它至少有一块相邻(4 个方向之一)砖块不会掉落时,它才不会落下。

我们会依次消除一些砖块。每当我们消除 (i, j) 位置时, 对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这个消除而落下。

返回一个数组表示每次消除操作对应落下的砖块数目。

测试样例

示例 1:
输入:
grid = [[1,0,0,0],[1,1,1,0]]
hits = [[1,0]]
输出: [2]
解释:
如果我们消除(1, 0)位置的砖块, 在(1, 1) 和(1, 2) 的砖块会落下。所以我们应该返回2。
示例 2:
输入:
grid = [[1,0,0,0],[1,1,0,0]]
hits = [[1,1],[1,0]]
输出:[0,0]
解释:
当我们消除(1, 0)的砖块时,(1, 1)的砖块已经由于上一步消除而消失了。所以每次消除操作不会造成砖块落下。注意(1, 0)砖块不会记作落下的砖块。
注意:

  • 网格的行数和列数的范围是[1, 200]。
  • 消除的数字不会超过网格的区域。
  • 可以保证每次的消除都不相同,并且位于网格的内部。
  • 一个消除的位置可能没有砖块,如果这样的话,就不会有砖块落下。

题解

反向思维+并查集
我们不妨把消除过程看成是反向的装砖过程,我们使用并查集来辅助。
先假设所有打下的砖块最初没有砌上,初始化并查集,记录与天花板相连的数量,然后根据逆序将砖头放上,更新并查集,更新的与天花板相连的砖块数量-1即为此次放砖头的答案。最后再将答案reverse便是最终的答案
需注意,如果放砖的地方本来没有砖就不需要放,直接将0push到答案集合中

并查集,开辟r * c+1的空间,网格中的坐标(i,j)对应的空间为i * c+j,然后r * c表示天花板。因此只要祖先是r * c的便是与天花板相连。
详细过程见代码

代码

class UF{
public:
    vector<int> parent;
    vector<int> size;
    UF(int row,int column){
        parent = vector<int>(row*column+1,0);
        size = vector<int>(row*column+1,1);
        for(int i=0; i<parent.size(); i++){
            parent[i] = i;
        }
    }
    int find(int x){
        while(x != parent[x]){
            x = parent[x];
        }
        return x;
    }
    int getBricksNum(int x){
        return size[find(x)];
    }
    bool Union(int x,int y){
        int pX = find(x);
        int pY = find(y);
        if(pX == pY)    return true;
        if(size[pX] > size[pY]){
            parent[pY] = pX;
            size[pX] += size[pY];
        }else{
            parent[pX] = pY;
            size[pY] += size[pX];
        }
        return true;
    }
};
class Solution {
public:
    vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {
        int n = hits.size();
        int row = grid.size(),column = grid[0].size();
        int dir[4][2] = {
            {-1,0},{0,-1},{1,0},{0,1}
        };
        for(int i=0; i<n; i++){			//将打掉的砖改成-1
            if(grid[hits[i][0]][hits[i][1]] == 1)
                grid[hits[i][0]][hits[i][1]] = -1;
        }
        UF uf = UF(row,column);
        for(int i=0; i<row; i++){		//初始化并查集
            for(int j=0; j<column; j++){
                if(grid[i][j] == 1){		//消除的砖就当没有了,因此只要考虑每有消除的砖
                    int id = i*column+j;
                    if(i==0)    uf.Union(id,row*column);
                    for(int k=0; k<2; k++){		//根据我们的遍历我们,我们只需要看上和左两个方向
                        if(i+dir[k][0]>=0 && i+dir[k][0]<row && j+dir[k][1]>=0 && j+dir[k][1]<column && grid[i+dir[k][0]][j+dir[k][1]]==1){
                            uf.Union(id,(i+dir[k][0])*column+j+dir[k][1]);
                        }
                    }
                }
            }
        }
        vector<int> ans;
        for(int i=n-1; i>=0; i--){		//反向装砖
            int preNum = uf.getBricksNum(row*column);
            int x = hits[i][0],y = hits[i][1];
            if(grid[x][y] == 0){
                ans.push_back(0);
                continue;
            }
            grid[x][y] = 1;
            for(int k=0; k<4; k++){
                if(x+dir[k][0]>=0 && x+dir[k][0]<row && y+dir[k][1]>=0 && y+dir[k][1]<column && grid[x+dir[k][0]][y+dir[k][1]]==1){
                    uf.Union(x*column+y,(x+dir[k][0])*column+y+dir[k][1]);
                }

            }
            if(x == 0){
                uf.Union(x*column+y,row*column);
            }
            ans.push_back(max(0,uf.getBricksNum(row*column)-preNum-1));			//本身消除的砖是不作为记录的,因此要-1,这也就有可能只有我们消除的砖是连接着天花板的,故-1可能出现-1的情况,我们取max
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

/*
并查集的反向思想
先假设所有打下的砖块最初没有砌上,初始化并查集,记录与天花板相连的数量,然后根据逆序将砖头放上,更新并查集,更新的与天花板相连的砖块数量-1即为此次放砖头的答案。最后再将答案reverse便是最终的答案
需注意,如果放砖的地方本来没有砖就不需要放,直接将0push到答案集合中

并查集,开辟r*c+1的空间,网格中的坐标(i,j)对应的空间为i*c+j,然后r*c表示天花板。因此只要祖先是r*c的便是与天花板相连
*/

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bricks-falling-when-hit
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值