• 并查集-打砖块-没懂


    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];
            }
    
    
        }
    }
  • 相关阅读:
    vue keep-alive的使用
    vscode 快捷键整理
    form表单的验证validator如何传递参数
    使用elementui 的validateField,resetFields,clearValidate的使用
    vue sass样式穿透实现
    部署node服务(在本地模拟环境进行部署)
    利用存css实现弧形边界
    koa mongoose 实践篇,各种必要的功能总结;
    koa mogoose 创建后台服务连接数据库并进行增删改查
    vue项目中 render函数直接操作html元素报错
  • 原文地址:https://www.cnblogs.com/jieyi/p/14285871.html
Copyright © 2020-2023  润新知