(This problem is the same as *Minimize Malware Spread*, with the differences bolded.)

In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j] = 1.

Some nodes initial are initially infected by malware.  Whenever two nodes are directly connected and at least one of those two nodes is infected by malware, both nodes will be infected by malware.  This spread of malware will continue until no more nodes can be infected in this manner.

Suppose M(initial) is the final number of nodes infected with malware in the entire network, after the spread of malware stops.

We will remove one node from the initial list, completely removing it and any connections from this node to any other node.  Return the node that if removed, would minimize M(initial).  If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index.

Example 1:

Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
Output: 0

Example 2:

Input: graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1]
Output: 1

Example 3:

Input: graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1]
Output: 1

Note:

  1. 1 < graph.length = graph[0].length <= 300
  2. 0 <= graph[i][j] == graph[j][i] <= 1
  3. graph[i][i] = 1
  4. 1 <= initial.length < graph.length
  5. 0 <= initial[i] < graph.length

这道题是之前那道 [Minimize Malware Spread](https://www.cnblogs.com/grandyang/p/11838844.html) 的拓展,实际上只是修改了一个地方,就是文中加粗的地方,说的是每次直接将这个结点去掉,而不是像之前的题目那样只是将其不当做感染源。那么二者到底有什么区别呢,实际上并不是在所有的 test case 上都有区别,只是部分会有。比如对于 graph=[[1,1,0],[1,1,1],[0,1,1]], initial=[0,1] 来说,可以发现结点的链接情况是 0-1-2,感染源结点是0和1,若是按之前题目的要求,移除0和1都不会减少最终感染个数,但是应该返回结点0,因为其 index 小。但是应用此题的条件,就一定要返回结点1,因为移除结点1之后,就断开了结点0和结点2的连接,最终只有病毒源结点0会保持感染状态,这就是二者的区别所在。解题思路完全可以在之前那道题 [Minimize Malware Spread](https://www.cnblogs.com/grandyang/p/11838844.html) 的基础上进行修改,最简单暴力的修改其实是新建一个 graph 的副本,然后当要删掉某个结点i的时候,将所有的 graph[i][:] 和 graph[:][i] 都赋值为0即可,这样修改邻接矩阵就相当于断开了结点i和其他所有结点之间的连接,博主亲测解法1和解法2都可以用此方法通过。当然也可以用其他的方法,这里博主将要删除的结点 num 带入到了子函数中,在 BFS 中遍历某个结点的所有邻接点的时候,只要多加个判断,跳过 num 结点也是可以的,参见代码如下:


解法一:

class Solution {
public:
int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
int mn = INT_MAX, res = 0;
unordered_set<int> infected(initial.begin(), initial.end());
for (int num : initial) {
infected.erase(num);
int cnt = helper(graph, infected, num);
if (cnt < mn || (cnt == mn && num < res)) {
mn = cnt;
res = num;
}
infected.insert(num);
}
return res;
}
int helper(vector<vector<int>>& graph, unordered_set<int> infected, int num) {
queue<int> q;
for (int num : infected) q.push(num);
while (!q.empty()) {
auto t = q.front(); q.pop();
for (int i = 0; i < graph[t].size(); ++i) {
if (i == num || graph[t][i] != 1 || infected.count(i)) continue;
infected.insert(i);
q.push(i);
}
}
return infected.size();
}
};

当然对于 DFS 的解法也可以做类似的处理,除了上面提到的建立 graph 副本并修改的方法之外,也可以用其他的方法。这里博主对于每个要移除的结点 num,先将其加入 visited 之中,然后在遍历的时候,只对非 num 结点调用递归函数,可以得到同样的结果,参见代码如下:


解法二:

class Solution {
public:
int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
int mn = INT_MAX, res = 0;
unordered_set<int> infected(initial.begin(), initial.end());
for (int num : initial) {
infected.erase(num);
int cnt = 0;
unordered_set<int> visited{{num}};
for (int cur : infected) {
if (cur != num) helper(graph, cur, visited, cnt);
}
if (cnt < mn || (cnt == mn && num < res)) {
mn = cnt;
res = num;
}
infected.insert(num);
}
return res;
}
void helper(vector<vector<int>>& graph, int cur, unordered_set<int>& visited, int& cnt) {
if (visited.count(cur)) return;
visited.insert(cur);
++cnt;
for (int i = 0; i < graph[cur].size(); ++i) {
if (graph[cur][i] != 1) continue;
helper(graph, i, visited, cnt);
}
}
};

讨论:博主本想将之前那道 [Minimize Malware Spread](https://www.cnblogs.com/grandyang/p/11838844.html) 的解法三也修改一下用到这里,但发现比较难修改,论坛虽然也有一些使用联合查找 Union Find 来做的解法,但是感觉都比较复杂,这里就不写了,若哪位看官大神可以将之前的解法三移植过来,请务必留言告诉博主哈~


Github 同步地址:

https://github.com/grandyang/leetcode/issues/928

类似题目:

Minimize Malware Spread

参考资料:

https://leetcode.com/problems/minimize-malware-spread-ii/

https://leetcode.com/problems/minimize-malware-spread-ii/discuss/217529/c%2B%2B-solution-bfs

https://leetcode.com/problems/minimize-malware-spread-ii/discuss/184645/Straightforward-DFS-Java-6-ms

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 928. Minimize Malware Spread II 最大程度上减少恶意软件的传播之二的更多相关文章

  1. [LeetCode] 924. Minimize Malware Spread 最大程度上减少恶意软件的传播

    In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j ...

  2. [Swift]LeetCode928. 尽量减少恶意软件的传播 II | Minimize Malware Spread II

    (This problem is the same as Minimize Malware Spread, with the differences bolded.) In a network of ...

  3. 【leetcode】924.Minimize Malware Spread

    题目如下: In a network of nodes, each node i is directly connected to another node j if and only if grap ...

  4. [Swift]LeetCode924.尽量减少恶意软件的传播 | Minimize Malware Spread

    In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j ...

  5. LeetCode刷题总结-数组篇(上)

    数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...

  6. 【LeetCode】227. Basic Calculator II 解题报告(Python)

    [LeetCode]227. Basic Calculator II 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: h ...

  7. 乘风破浪:LeetCode真题_040_Combination Sum II

    乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum ...

  8. [leetcode]Unique Binary Search Trees II @ Python

    原题地址:https://oj.leetcode.com/problems/unique-binary-search-trees-ii/ 题意:接上一题,这题要求返回的是所有符合条件的二叉查找树,而上 ...

  9. LeetCode 445——两数相加 II

    1. 题目 2. 解答 2.1 方法一 在 LeetCode 206--反转链表 和 LeetCode 2--两数相加 的基础上,先对两个链表进行反转,然后求出和后再进行反转即可. /** * Def ...

随机推荐

  1. 132、Java面向对象之static关键字四(定义一个数学的加法操作)

    01.代码如下: package TIANPAN; class MyMath { // 数学操作类,类中没有属性 public static int add(int x, int y) { // 只是 ...

  2. CSS - 伪类和伪元素

    1. CSS3中 :Pseudo-classes 伪类 ::Pseudo-elements 伪元素 2. 为什么叫伪类和伪元素? 伪类的效果可以通过添加一个实际的类来达到,而伪元素的效果则需要通过添加 ...

  3. ClientDataSet.locate报错问题

    数据集循环之后如果使用locate定位,需要首先将数据集first

  4. 如何反编译MIPS64伪代码?用Ghidra

    在分析固件时,碰到MIPS64架构的程序会很头疼,虽然用IDA能够反编译出汇编代码,但是没办法F5一键反编译成伪代码,如果单看汇编,看久了脑壳痛. 后来Google到了一个好工具,Ghidra,发音和 ...

  5. scikit_learn (sklearn)库中NearestNeighbors(最近邻)函数的各参数说明

    NearestNeighbors(n_neighbors=5, radius=1.0, algorithm='auto', leaf_size=30, metric='minkowski', p=2, ...

  6. jmeter学习笔记---循环控制器计数器函数助手

    循环控制器与计数器,以及函数助手需要配合使用,实现循环 循环控制器的“循环次数”输入最大循环次数的参数 计数器:除输入最大值外,还需要输入“引用名称”,供后续请求使用 请求中,如果需要实现循环,需要借 ...

  7. Jquery属性操作、添加类

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. GetHub上很实用的几个Demo

    手机号匹配的正则表达式:https://github.com/VincentSit/ChinaMobilePhoneNumberRegex/blob/master/README-CN.md FEBS- ...

  9. vue :src 不显示的解决方案

    一定要将静态资源引入 [ require("@/assets/") ],绑定到 模型绑定的:src 数据中 动态的数据才能有效   <template>   <d ...

  10. 【LeetCode】排列硬币

    [问题]你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币.给定一个数字 n,找出可形成完整阶梯行的总行数.n 是一个非负整数,并且在32位有符号整型的范围内. [ ...