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);
}
}

  

图-连通分量-DFS-749. 隔离病毒的更多相关文章

  1. Java实现 LeetCode 749 隔离病毒(DFS嵌套)

    749. 隔离病毒 病毒扩散得很快,现在你的任务是尽可能地通过安装防火墙来隔离病毒. 假设世界由二维矩阵组成,0 表示该区域未感染病毒,而 1 表示该区域已感染病毒.可以在任意 2 个四方向相邻单元之 ...

  2. 图-连通分量-DFS-并查集-695. 岛屿的最大面积

    2020-03-15 16:41:45 问题描述: 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二 ...

  3. [Swift]LeetCode749. 隔离病毒 | Contain Virus

    A virus is spreading rapidly, and your task is to quarantine the infected area by installing walls. ...

  4. [数据结构]图的DFS和BFS的两种实现方式

    深度优先搜索 深度优先搜索,我们以无向图为例. 图的深度优先搜索(Depth First Search),和树的先序遍历比较类似. 它的思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发, ...

  5. UVa-208 Firetruck (图的DFS)

    UVA-208 天道好轮回.UVA饶过谁. 就是一个图的DFS. 不过这个图的边太多,要事先判一下起点和终点是否联通(我喜欢用并查集),否则会TLE. #include <iostream> ...

  6. 图的DFS和BFS(邻接表)

    用C++实现图的DFS和BFS(邻接表) 概述   图的储存方式有邻接矩阵和邻接表储存两种.由于邻接表的实现需要用到抽象数据结构里的链表,故稍微麻烦一些.C++自带的STL可以方便的实现List,使算 ...

  7. 图的DFS与BFS

    图的DFS与BFS(C++) 概述 大一学生,作为我的第一篇Blog,准备记录一下图的基本操作:图的创建与遍历.请大佬多多包涵勿喷. 图可以采用邻接表,邻接矩阵,十字链表等多种储存结构进行储存,这里为 ...

  8. Java数据结构——图的DFS和BFS

    1.图的DFS: 即Breadth First Search,深度优先搜索是从起始顶点开始,递归访问其所有邻近节点,比如A节点是其第一个邻近节点,而B节点又是A的一个邻近节点,则DFS访问A节点后再访 ...

  9. 1013 Battle Over Cities (25分) 图的连通分量+DFS

    题目 It is vitally important to have all the cities connected by highways in a war. If a city is occup ...

随机推荐

  1. python3下BeautifulSoup练习一(爬取小说)

    上次写博客还是两个月以前的事,今天闲来无事,决定把以前刚接触python爬虫时的一个想法付诸行动:就是从网站上爬取小说,这样可以省下好多流量(^_^). 因为只是闲暇之余写的,还望各位看官海涵:不足之 ...

  2. seaJs模块化开发简单入门

    随着前端技术的日益成熟,功能越来越丰富强大,规范也越来越健全,在这样的背景环境下很快便有了CommonJs.AMD.CMD等一系列规范,使前端发开趋向模块化.规范化.CMD模块化的代表之一就是国内开发 ...

  3. C++走向远洋——41(深复制体验,3,)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  4. 难道同事:Java 方法调用到底是传值还是传引用

    Java 方法调用中的参数是值传递还是引用传递呢?相信每个做开发的同学都碰到过传这个问题,不光是做 Java 的同学,用 C#.Python 开发的同学同样肯定遇到过这个问题,而且很有可能不止一次. ...

  5. JavaScript实现队列结构(Queue)

    JavaScript实现队列结构(Queue) 一.队列简介 队列是是一种受限的线性表,特点为先进先出(FIFO:first in first out). 受限之处在于它只允许在表的前端(front) ...

  6. 正式学习MVC 03

    1.View -> Controller的数据通信 1) 通过url查询字符串 public ActionResult Index(string user) { return Content(u ...

  7. CSS Sprite雪碧图的应用

    CSS雪碧图,即CSS Sprite,也有人叫它CSS精灵图,是一种图像拼合技术.该方法是将多个小图标和背景图像合并到一张图片上,然后利用CSS的背景定位来显示需要显示的图片部分. 雪碧图的使用场景 ...

  8. 【JavaScript】DOM之Document对象

    JS(JavaScript) 一.Document对象 1.Document对象是什么 Document对象 是DOM的基本规范也是重要的对象之一,以访问,更新页面内容的属性和方法通过conslie. ...

  9. 前端每日实战:60# 视频演示如何用纯 CSS 创作一块乐高积木

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/qKKqrv 可交互视频 此视频是可 ...

  10. 对JS中事件委托的理解

    什么是事件委托: 事件委托——给父元素绑定事件,用来监听子元素的冒泡事件,并找到是哪个子元素的事件.(不理解冒泡的可以去百度下) 定义:利用事件冒泡处理动态元素事件绑定的方法,专业术语叫事件委托. 使 ...