PS:算法并非原创,仅作个人学习使用,侵删

题目描述

算法分析
经过几题图论和并查集的磨练,我知道这道题肯定能用并查集的方法做出来。
但是具体怎么使用并查集之类的图论算法呢?一开始我没什么思路,这道题目有点类似泡泡龙游戏,我并没有什么很好的思路。
后来看了官方的题解,和顶部相连的砖块不会掉落,只能击打实现砖块消失。并查集将两个连通分量合并,击打砖块则是让一个连通分量分成两部分,所以需要“逆用”并查集。
使用的方法就是根据hit的元素顺序将对应位置的砖块顺序击打(gird中对应位置值置为0),之后进行连通分量的统计,之后再将砖块逆序添加,每添加一个,计算被合并的连通分量中的元素个数,这就是每次被击落的砖块个数。
连通分量的计算方式也可用DFS和BFS来计算得出。

代码实现

【C】

/*
反向思考,使用并查集方式
*/
typedef struct {int x, y;
} point_t;const point_t wards[] = { -1, 0, 0, -1, 1, 0, 0, 1 };
int find(int ancestor[], int n) {if (ancestor[n] == -1 || ancestor[n] == n) return ancestor[n] = n;return ancestor[n] = find(ancestor, ancestor[n]);
}
void merge(int ancestor[], int children[], int a, int b) {int pa = find(ancestor, a), pb = find(ancestor, b);if (pa == pb) return;for (int tmp = pa; pa > pb; pa = pb, pb = tmp) {}ancestor[pa] = pb, children[pb] += children[pa] + 1;
}
int *hitBricks(int **grid, int gridSize, int *gridColSize, int **hits, int hitsSize, int *hitsColSize,int *returnSize) {int *ans = malloc((*returnSize = hitsSize) * sizeof(int));int m = gridSize, n = *gridColSize, stable = m * n, map[m][n], ancestor[stable + 1], children[stable + 1];// 并查集初始化memset(ancestor, -1, sizeof(ancestor));memset(children, 0, sizeof(children));// 地图拷贝for (int i = 0; i < m; ++i) {memcpy(map[i], grid[i], n * sizeof(int));}// 打破所有需要的砖块for (int i = 0; i < hitsSize; ++i) {map[hits[i][0]][hits[i][1]] = 0;}// 靠近墙面砖块标记为稳定for (int i = 0; i < n; ++i) {if (map[0][i]) merge(ancestor, children, i, stable);}// 对剩余的砖块标记for (int i = 1; i < m; ++i) {for (int j = 0; j < n; ++j) {if (!map[i][j]) continue;for (int k = 0; k < sizeof(wards) / sizeof(wards[0]); ++k) {point_t next = (point_t) { i + wards[k].x, j + wards[k].y };if (next.x < 0 || next.x >= m || next.y < 0 || next.y >= n || !map[next.x][next.y]) continue;merge(ancestor, children, i * n + j, next.x * n + next.y);}}}// 逆序补回砖块for (int i = hitsSize - 1, last = children[stable]; i >= 0; --i, last = children[stable]) {point_t *hit = ( point_t * )hits[i];// 砖块补回if (grid[hit->x][hit->y]) {map[hit->x][hit->y] = 1;for (int j = 0; j < sizeof(wards) / sizeof(wards[0]); ++j) {point_t next = (point_t) { hit->x + wards[j].x, hit->y + wards[j].y };if (next.x < 0 || next.x >= m || next.y < 0 || next.y >= n || !map[next.x][next.y]) continue;merge(ancestor, children, hit->x * n + hit->y, next.x * n + next.y);}// 靠近墙面的砖块需要标记为牢固if (!hit->x) merge(ancestor, children, hit->x * n + hit->y, stable);}ans[i] = children[stable] > last ? children[stable] - last - 1 : 0;}return ans;
}

C语言参考网址

【C++】

/*
BFS方式
*/
class Solution {public:int dir[4][2]={{1,0},{-1,0},{0,-1},{0,1}};int bfs(vector<vector<int>>& grid,int x,int y,int val,int nval){int m = grid.size(),n = grid[0].size();grid[x][y] = nval;queue<pair<int,int>>que;que.push({x,y});int cnt = 0;while(que.size()){pair<int,int> front = que.front();que.pop();cnt++;x = front.first;y = front.second;for(int i =0;i<4;i++){int _x = x+dir[i][0];int _y = y+dir[i][1];if(_x<0||_y<0||_x>=m||_y>=n||grid[_x][_y]!=val){continue;}grid[_x][_y] = nval;que.push({_x,_y});}}return cnt;}vector<int> hitBricks(vector<vector<int>>& grid, vector<vector<int>>& hits) {if(hits.size()==0)return {};vector<int> ans(hits.size(),0);int m = grid.size(),n = grid[0].size();for(int i = 0;i<hits.size();i++){grid[hits[i][0]][hits[i][1]]--;}for(int i =0;i<n;i++){if(grid[0][i]==1){bfs(grid,0,i,1,2);}}for(int i =hits.size()-1;i>=0;i--){int x = hits[i][0];int y = hits[i][1];grid[x][y]++;int num = 0;if(grid[x][y]==1){//判断周围是否可以和顶部连接bool sat = false;if(x==0){sat = true;}for(int j =0;j<4&&!sat;j++){int _x = x+dir[j][0];int _y = y+dir[j][1];if(_x<0||_y<0||_x>=m||_y>=n||grid[_x][_y]!=2){continue;}sat = true;break;}//如果可以连接,则将周围相邻的值为1的方框置为2,表示与顶部相连if(sat){num = bfs(grid,x,y,1,2)-1;}}ans[i] = num;}return ans;}
};

C++参考网址

【Java】

/*
官方题解,并查集解法
*/
public class Solution {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 中的砖头全部击碎,通常算法问题不能修改输入数据,这一步非必需,可以认为是一种答题规范int[][] copy = new int[rows][cols];for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {copy[i][j] = grid[i][j];}}// 把 copy 中的砖头全部击碎for (int[] hit : hits) {copy[hit[0]][hit[1]] = 0;}// 第 2 步:建图,把砖块和砖块的连接关系输入并查集,size 表示二维网格的大小,也表示虚拟的「屋顶」在并查集中的编号int size = rows * cols;UnionFind unionFind = new UnionFind(size + 1);// 将下标为 0 的这一行的砖块与「屋顶」相连for (int j = 0; j < cols; j++) {if (copy[0][j] == 1) {unionFind.union(j, size);}}// 其余网格,如果是砖块向上、向左看一下,如果也是砖块,在并查集中进行合并for (int i = 1; i < rows; i++) {for (int j = 0; j < 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 中补回砖块,把每一次因为补回砖块而与屋顶相连的砖块的增量记录到 res 数组中int hitsLen = hits.length;int[] res = new int[hitsLen];for (int i = hitsLen - 1; i >= 0; i--) {int x = hits[i][0];int y = hits[i][1];// 注意:这里不能用 copy,语义上表示,如果原来在 grid 中,这一块是空白,这一步不会产生任何砖块掉落// 逆向补回的时候,与屋顶相连的砖块数量也肯定不会增加if (grid[x][y] == 0) {continue;}// 补回之前与屋顶相连的砖块数int origin = unionFind.getSize(size);// 注意:如果补回的这个结点在第 1 行,要告诉并查集它与屋顶相连(逻辑同第 2 步)if (x == 0) {unionFind.union(y, size);}// 在 4 个方向上看一下,如果相邻的 4 个方向有砖块,合并它们for (int[] direction : DIRECTIONS) {int newX = x + direction[0];int newY = y + direction[1];if (inArea(newX, newY) && copy[newX][newY] == 1) {unionFind.union(getIndex(x, y), getIndex(newX, newY));}}// 补回之后与屋顶相连的砖块数int current = unionFind.getSize(size);// 减去的 1 是逆向补回的砖块(正向移除的砖块),与 0 比较大小,是因为存在一种情况,添加当前砖块,不会使得与屋顶连接的砖块数更多res[i] = Math.max(0, current - origin - 1);// 真正补上这个砖块copy[x][y] = 1;}return res;}/*** 输入坐标在二维网格中是否越界** @param x* @param y* @return*/private boolean inArea(int x, int y) {return x >= 0 && x < rows && y >= 0 && y < cols;}/*** 二维坐标转换为一维坐标** @param x* @param y* @return*/private int getIndex(int x, int y) {return x * cols + y;}private class UnionFind {/*** 当前结点的父亲结点*/private int[] parent;/*** 以当前结点为根结点的子树的结点总数*/private int[] size;public UnionFind(int n) {parent = new int[n];size = new int[n];for (int i = 0; i < n; i++) {parent[i] = i;size[i] = 1;}}/*** 路径压缩,只要求每个不相交集合的「根结点」的子树包含的结点总数数值正确即可,因此在路径压缩的过程中不用维护数组 size** @param x* @return*/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;// 在合并的时候维护数组 sizesize[rootY] += size[rootX];}/*** @param x* @return x 在并查集的根结点的子树包含的结点总数*/public int getSize(int x) {int root = find(x);return size[root];}}
}

Java参考网址

【python】

#并查集解法
class Solution:def hitBricks(self, grid, hits):m, n = len(grid), len(grid[0])ans = [-1]*len(hits)def dfs(x, y):if 0 <= x < m and 0 <= y < n and grid[x][y] == 1:grid[x][y] = 2ans = 1 + dfs(x + 1, y) + dfs(x - 1, y) + dfs(x, y + 1) + dfs(x, y - 1)return ansreturn 0def is_stable(x, y):if x == 0: return Trueif x + 1 < m and grid[x + 1][y] == 2: return Trueif x - 1 >= 0 and grid[x - 1][y] == 2: return Trueif y + 1 < n and grid[x][y + 1] == 2: return Trueif y - 1 >= 0 and grid[x][y - 1] == 2: return Truereturn False# 模拟最终的残局for x, y in hits:grid[x][y] -= 1# 标记稳定砖块。 注意不标记被打掉的砖块,因此这一步需要在“模拟最终的残局”之后for y in range(n):dfs(0, y)# 倒推for i in range(len(hits) - 1, -1,-1):x, y = hits[i]grid[x][y] += 1# 如果不稳定,打掉也没啥影响if not is_stable(x, y) or grid[x][y] == 0:ans[i] = 0else:# 否则 dfs 计算联通图大小,这里的联通指的是值为 1。# 实际指的是添加了 (x,y) 砖块之后,这些值为 1 的砖块会变成稳定砖块(我们用 2 表示)# 由于我们是反推,因此就是移除 (x, y) 砖块之后, 这些稳定的砖块会变成不稳定(掉落)ans[i] = dfs(x, y) - 1return ans

python参考网址

leetcode 803.打砖块(C/C++/Java/python)相关推荐

  1. leetcode 803 打砖块

    前言 题目:803. 打砖块 参考题解:打砖块-力扣官方题解 提交代码 这道题目还是有难度的.难度有两点: 倒着使用并查集. 使用一个虚拟的根节点. #include <vector> # ...

  2. LeetCode 803. 打砖块(并查集)

    文章目录 1. 题目 2. 解题 1. 题目 有一个 m x n 的二元网格,其中 1 表示砖块,0 表示空白. 砖块 稳定(不会掉落)的前提是: 一块砖直接连接到网格的顶部,或者 至少有一块相邻(4 ...

  3. LeetCode 319. Bulb Switcher--C++,java,python 1行解法--数学题

    LeetCode 319. Bulb Switcher–C++,java,python 1行解法 LeetCode题解专栏:LeetCode题解 LeetCode 所有题目总结:LeetCode 所有 ...

  4. leetcode-292-Nim游戏(java|python)

    title: leetcode-292-Nim游戏(java|python) date: 2019-10-12 21:18:57 categories: leetcode tags: leetcode ...

  5. leetcode-136-只出现一次的数字(java|python)

    title: leetcode-136-只出现一次的数字(java|python) date: 2019-09-25 19:12:13 mathjax: true categories: leetco ...

  6. java python算法_用Python,Java和C ++示例解释的排序算法

    java python算法 什么是排序算法? (What is a Sorting Algorithm?) Sorting algorithms are a set of instructions t ...

  7. CUDA,C++,Java,Python,Fortran运行速度比较

    通过计算100万以内素数的运行时间比较这5种语言的运行速度. 每种语言运行30次,取平均值.由于python和fortran的运行速度和Java和C++运行差距过大,python只计算了3次,Fort ...

  8. 编程笔试(解析及代码实现):国内各大银行(招商银行/浦发银行等)在线笔试常见题目(猴子吃桃/字符串逆序输出/一段话输出字的个数/单词大小转换等)及其代码实现(Java/Python/C#等)之详细攻略

    编程笔试(解析及代码实现):国内各大银行(招商银行/浦发银行等)在线笔试常见题目(猴子吃桃/字符串逆序输出/一段话输出字的个数/单词大小转换等)及其代码实现(Java/Python/C#等)之详细攻略 ...

  9. BigData:大数据开发的简介、核心知识(linux基础+Java/Python编程语言+Hadoop{HDFS、HBase、Hive}+Docker)、经典场景应用之详细攻略

    BigData:大数据开发的简介.核心知识(linux基础+Java/Python编程语言+Hadoop{HDFS.HBase.Hive}+Docker).经典场景应用之详细攻略 BigData:大数 ...

  10. Java | Python 流程控制对比

      Java Python   数据类型 byte.short.int.long.float.double.char.boolean 数组.类.接口 Number(数字): int.float.boo ...

最新文章

  1. SQLSERVER 查询存储过程内容,主要是通过关键词查询相关的存储过程使用
  2. ABAP 向上取整和向下取整 CEIL FLOOR
  3. Python模块和包
  4. MapReduce-流量统计求和-Reducer和JobMain代码编写
  5. ssh登录命令(转)
  6. 2020计算机领域前沿热门技术,CFP: ICPCSEE 2020 (SCI or EI Indexd) 第6届国际计算机前沿大会...
  7. jQueryUI modal dialog does not show close button (x) JQueryUI和BootStrap混用时候,右上角关闭按钮显示不出图标的解决办法...
  8. 优秀案例UI素材模板|深层解析iPhone手机APP页面怎么设计?
  9. digiKam 6.1.0 发布,相片管理工具
  10. 室内定位---UWB测距及定位原理
  11. Spark 机器学习 —— ALS
  12. windows7 安装向导
  13. cadence的工艺角仿真、蒙特卡洛仿真、PSRR
  14. coffeescript html5,深入浅出CoffeeScript
  15. 口头禅多多......
  16. CISCO交换机3850升级
  17. Word VBA自动排版(2)-通过自动查找替换去除叠字
  18. python读取、保存图片的方法
  19. 从ES6到ES10的新特性万字大总结
  20. Heartbleed心脏出血漏洞原理分析

热门文章

  1. 3d布衣天下1手机调试html,真精华布衣天下3d
  2. 基于vue的前端UI表单设计器
  3. excel转vcf 易语言免费版
  4. Zynga公布2019年第三季度财务业绩
  5. 1.4树莓派SSH远程登录
  6. 长沙社区团购独角兽《兴盛优选》 18k 面试题记录,已拿offer!
  7. deepin关机卡桌面_有了MyDock,我也有了一个MacBook桌面
  8. 使用基于全志D1-H的LicheeRV的 86 Panel 与 Tina BSP 实现 RGB 与 SPI 双屏显示
  9. 电脑无故关机,出现提示:从异常关机中恢复 bluescreen.........
  10. Mac outlook设置HTML,设置苹果MAC 端outlook客户端说明