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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。