zoukankan      html  css  js  c++  java
  • 并查集-打砖块-没懂

    public class test11 {
        public static void main(String[] args) {
            int [][]grid={{1,0,0,0},{1,1,0,0}};
            int [][]hits={{1,1},{1,0}};
            int []result=hitBricks(grid, hits);
            int x=0;
        }
        public static  int rows;
        public static  int cols;
    
        public static final int[][] DIRECTIONS = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
    
    
        public static int[] hitBricks(int[][] grid, int[][] hits) {
            rows = grid.length;
            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 中,这一块是空白,这一步不会产生任何砖块掉落
                // 逆向补回的时候,与屋顶相连的砖块数量也肯定不会增加
                 // 补回之前与屋顶相连的砖块数
                int origin = unionFind.getSize(size);
                if (grid[x][y] == 0) {
                    continue;
                }
    
               
    
                // 注意:如果补回的这个结点在第 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
         */
        public static boolean inArea(int x,int y){
            if(x<0||y<0||x>=rows||y>=col){
                return false;
            }
            return true;
        }
    
        /**
         * 二维坐标转换为一维坐标
         *
         * @param x
         * @param y
         * @return
         */
        private static int getIndex(int x, int y) {
            return x * cols + y;
        }
    
        public static class UnionFind{
            //当前节点的父亲节点
            public int[]parent;
            //以当前节点为根节点的子节点数量
            public 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;
                }
            }
            //寻找根节点
            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;
               size[rootY]+=size[rootX];
            }
            
            public int getSize(int x) {
                int root = find(x);
                return size[root];
            }
    
    
        }
    }
  • 相关阅读:
    swift 第十四课 可视化view: @IBDesignable 、@IBInspectable
    swift 第十三课 GCD 的介绍和使用
    swift 第十二课 as 的使用方法
    swift 第十一课 结构体定义model类
    swift 第十课 cocopod 网络请求 Alamofire
    swift 第九课 用tableview 做一个下拉菜单Menu
    swift 第八课 CollectView的 添加 footerView 、headerView
    swift 第七课 xib 约束的优先级
    swift 第六课 scrollview xib 的使用
    swift 第五课 定义model类 和 导航栏隐藏返回标题
  • 原文地址:https://www.cnblogs.com/jieyi/p/14285871.html
Copyright © 2011-2022 走看看