zoukankan      html  css  js  c++  java
  • 图-连通分量-DFS-749. 隔离病毒

    2020-03-17 21:56:20

    问题描述:

    病毒扩散得很快,现在你的任务是尽可能地通过安装防火墙来隔离病毒。

    假设世界由二维矩阵组成,0 表示该区域未感染病毒,而 1 表示该区域已感染病毒。可以在任意 2 个四方向相邻单元之间的共享边界上安装一个防火墙(并且只有一个防火墙)。

    每天晚上,病毒会从被感染区域向相邻未感染区域扩散,除非被防火墙隔离。现由于资源有限,每天你只能安装一系列防火墙来隔离其中一个被病毒感染的区域(一个区域或连续的一片区域),且该感染区域对未感染区域的威胁最大且保证唯一。

    你需要努力使得最后有部分区域不被病毒感染,如果可以成功,那么返回需要使用的防火墙个数; 如果无法实现,则返回在世界被病毒全部感染时已安装的防火墙个数。

    示例 1:

    输入: grid = 
    [[0,1,0,0,0,0,0,1],
    [0,1,0,0,0,0,0,1],
    [0,0,0,0,0,0,0,1],
    [0,0,0,0,0,0,0,0]]
    输出: 10
    说明:
    一共有两块被病毒感染的区域: 从左往右第一块需要 5 个防火墙,同时若该区域不隔离,晚上将感染 5 个未感染区域(即被威胁的未感染区域个数为 5);
    第二块需要 4 个防火墙,同理被威胁的未感染区域个数是 4。因此,第一天先隔离左边的感染区域,经过一晚后,病毒传播后世界如下:
    [[0,1,0,0,0,0,1,1],
    [0,1,0,0,0,0,1,1],
    [0,0,0,0,0,0,1,1],
    [0,0,0,0,0,0,0,1]]
    第二天,只剩下一块未隔离的被感染的连续区域,此时需要安装 5 个防火墙,且安装完毕后病毒隔离任务完成。

    示例 2:

    输入: grid = 
    [[1,1,1],
    [1,0,1],
    [1,1,1]]
    输出: 4
    说明: 
    此时只需要安装 4 面防火墙,就有一小区域可以幸存,不被病毒感染。
    注意不需要在世界边界建立防火墙。 

    示例 3:

    输入: grid = 
    [[1,1,1,0,0,0,0,0,0],
    [1,0,1,0,1,1,1,1,1],
    [1,1,1,0,0,0,0,0,0]]
    输出: 13
    说明: 
    在隔离右边感染区域后,隔离左边病毒区域只需要 2 个防火墙了。 

    说明:

    grid 的行数和列数范围是 [1, 50]。
    grid[i][j] 只包含 0 或 1 。
    题目保证每次选取感染区域进行隔离时,一定存在唯一一个对未感染区域的威胁最大的区域。

    问题求解:

    本题可以看作是一条模拟题,使用dfs模拟每天发生的状况,理解题目含义不难,比较困难的是如何使用代码高效整洁的实现。

    求连通分量很容易就想到使用DFS,并且通过DFS不仅可以得到连通分量,还可以得到该连通分量所有的邻接未感染区域,另外还能够得到该连通分量需要的墙的数目。

    注意,这里墙的数目是连通分量与外界边的个数,而不是邻接未感染区域。

    考虑最坏情况需要m + n天会全部感染,因此最坏情况下需要模拟m + n天。

    时间复杂度:O(mn*(m + n))

        List<List<Integer>> areas = new ArrayList<>();
        List<Set<Integer>> neighs = new ArrayList<>();
        int wall = 0;
        Set<Integer> used = new HashSet<>();
        
        int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        public int containVirus(int[][] grid) {
            int res = 0;
            int m = grid.length;
            int n = grid[0].length;
            for (int k = 0; k < m + n; k++) {
                int idx = -1;
                int curr_wall = -1;
                areas.clear();
                neighs.clear();
                used.clear();
                for (int i = 0; i < m; i++) {
                    for (int j = 0; j < n; j++) {
                        if (grid[i][j] == 1 && !used.contains(i * n + j)) {
                            List<Integer> area = new ArrayList<>();
                            Set<Integer> neigh = new HashSet<>();
                            wall = 0;
                            dfs(grid, i, j, area, neigh);
                            if (wall == 0) continue;
                            if (idx == -1 || neighs.get(idx).size() < neigh.size()) {
                                idx = neighs.size();
                                curr_wall = wall;
                            }  
                            areas.add(area);
                            neighs.add(neigh);
                        }
                    }
                }
                if (idx == -1) break;
                res += curr_wall;
                for (int i = 0; i < areas.size(); i++) {
                    List<Integer> area = areas.get(i);
                    Set<Integer> neigh = neighs.get(i);
                    if (i == idx) {
                        for (int item : area) {
                            int x = item / n;
                            int y = item % n;
                            grid[x][y] = 2;
                        }
                    }
                    else {
                        for (int nei : neigh) {
                            int x = nei / n;
                            int y = nei % n;
                            grid[x][y] = 1;
                        }
                    }
                }
            }
            
            return res;
        }
        
        private void dfs(int[][] grid, int i, int j, List<Integer> area, Set<Integer> neigh) {
            int m = grid.length;
            int n = grid[0].length;
            used.add(i * n + j);
            area.add(i * n + j);
            for (int[] dir : dirs) {
                int ni = i + dir[0];
                int nj = j + dir[1];
                if (ni < 0 || ni >= m || nj < 0 || nj >= n || used.contains(ni * n + nj) || grid[ni][nj] == 2) continue;
                if (grid[ni][nj] == 0) {
                    neigh.add(ni * n + nj);
                    wall += 1;
                }
                if (grid[ni][nj] == 1) dfs(grid, ni, nj, area, neigh);
            }
        }
    

      

  • 相关阅读:
    Undo/Redo的C#实现方式
    c#中@符号作用
    面向对象理解随笔
    C# 面向对象定义常量,属性,方法
    c# 字段和属性
    面向对象思想
    用C表达面向对象语言的机制——C#版
    判断生日
    查找字符
    被7整除
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/12513870.html
Copyright © 2011-2022 走看看