[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] = 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. 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.
Note that if a node was removed from the initial list of infected nodes, it may still be infected later as a result of the malware spread.
Example 1:
Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
Output: 0
Example 2:
Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
Output: 0
Example 3:
Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
Output: 1
Note:
1 < graph.length = graph[0].length <= 3000 <= graph[i][j] == graph[j][i] <= 1graph[i][i] = 11 <= initial.length < graph.length0 <= initial[i] < graph.length
这道题说是让我们最大程度的减少恶意软件的传播,谈到这个话题,博主最先想到的就是红衣教主周鸿祎,当年的 3Q 大战的场景还历历在目,将杀毒软件完全免费确实是造福了用户。回到本题吧,这里给了一个二维数组 graph,其中 graph[i][j] 的值表示结点i和结点j是否相连,1为相连,0为不连,这就是邻接矩阵啊,已经帮我们建立好了,遍历的时候就可以直接使用了。还给了一个数组 initial,里面是病毒源,所有跟病毒源相连接的结点都会被感染,现在问若从病毒源中去掉哪个结点,会使得感染结点的数量最大程度的减少,若出现平局,则返回结点序号较小的那个。那么实际上这道题的本质还是遍历这个无向图,遍历的方法就有 DFS 和 BFS 两种。这里先来看 BFS 的解法,既然要在病毒源中去掉一个结点,由于不知道该去掉哪个结点,就遍历所有的情况,每次去掉一个不同的结点,然后剩下的病毒源结点就是起点,都排入队列中开始遍历,一般来说迭代的解法不需要用子函数,但这里为了使程序的结构更加清晰,还是使用了子函数,这里的 BFS 遍历有向图的写法就不多解释,差不多都是一样的写法,得到了所有可以被感染的结点个数 cnt 之后,跟全局最小值 mn 比较,假如 cnt 小于 mn,或者二者相等但是当前去掉的结点 num 序号小于 res 时,需要更新 mn 和 res,参见代码如下:
解法一:
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);
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) {
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 (graph[t][i] != 1 || infected.count(i)) continue;
infected.insert(i);
q.push(i);
}
}
return infected.size();
}
};
当然也可以使用 DFS 的写法,但是为了避免写两个子函数,需要在递归的子函数中带上当前结点这个参数,那么在调用的时候就不能像 BFS 那么简单了,而是要定义一些变量,比如 cnt,还有 HashSet,而且要对每一个剩余的病毒源结点调用递归函数,其他部分跟上的解法没啥区别,参见代码如下:
解法二:
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;
for (int cur : infected) {
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);
}
}
};
这道题也可以使用联合查找 Union Find 来做,因为 UF 算法可以将属于同一个群组的结点归类,使用一个 root 数组和一个 findRoot 函数,对于同一个群组的结点,调用 findRoot 函数会得到相同的祖先结点。这里还需要使用两个数组 area 和 malware,其中 area[i] 就表示祖先结点是i的群组中的结点个数,malware[i] 表示祖先结点是i的某个感染群组中的结点个数。首先初始化 root 数组,然后遍历 graph,对于每个 graph[i][j] 为1的位置,建立 root 数组的映射。注意这里跟之前有些不同的是,不能直接 root[i] = j,而是要分别对i和j调用 findRoot 函数,一定要找到i和j所属群组的祖先结点,然后对其建立映射。之后再遍历所有结点,找到每个结点的祖先结点,并在 area 数组中进行累加,同理,遍历所有的感染源结点,找到每个感染源结点的祖先结点,并在 malware 数组中进行累加。新建结果 res 为一个 pair 对儿,分别为去掉某个感染源结点后还会感染的结点总个数和那个去掉的结点序号,初始化为1和0,然后此时遍历所有的感染源结点,需要找到只有一个感染源结点的感染结点群组,想想为什么,因为多个感染源结点相连的话,不管去掉哪个,最终传染的结点个数都是相同的,所以若某个感染群组只有一个感染源的话,去掉这个感染源结点,就会拯救很多结点不被感染,而该群组的结点个数越多,表示去掉该感染源结点的效果越好,这里将结点个数取相反数,然后比较取最小值,得到的就是群结点数最多的情况,假如不存在只有一个感染源结点的群组,那么就返回序号最小的那个感染源结点即可,参见代码如下:
解法三:
class Solution {
public:
int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
int n = graph.size();
vector<int> root(n), area(n), malware(n), res{1, 0};
for (int i = 0; i < n; ++i) root[i] = i;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (graph[i][j] == 1) root[findRoot(root, i)] = findRoot(root, j);
}
}
for (int i = 0; i < n; ++i) ++area[findRoot(root, i)];
for (int i : initial) ++malware[findRoot(root, i)];
for (int i : initial) {
res = min(res, {(malware[findRoot(root, i)] == 1 ) * (-area[findRoot(root, i)]), i});
}
return res[1];
}
int findRoot(vector<int>& root, int i) {
return i == root[i] ? i : findRoot(root, root[i]);
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/924
参考资料:
https://leetcode.com/problems/minimize-malware-spread/
https://leetcode.com/problems/minimize-malware-spread/discuss/181116/Java-BFS
https://leetcode.com/problems/minimize-malware-spread/discuss/181129/C%2B%2BPython-Union-Found
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 924. Minimize Malware Spread 最大程度上减少恶意软件的传播的更多相关文章
- [LeetCode] 928. Minimize Malware Spread II 最大程度上减少恶意软件的传播之二
(This problem is the same as Minimize Malware Spread, with the differences bolded.) In a network of ...
- 【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 ...
- [Swift]LeetCode928. 尽量减少恶意软件的传播 II | Minimize Malware Spread II
(This problem is the same as Minimize Malware Spread, with the differences bolded.) In a network of ...
- [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 ...
- 14.5.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁
14.5.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁 这个部分建立在概念信息关于deadlocks 在章节 14.5.5.2, "D ...
- 14.3.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁
14.3.5.3 How to Minimize and Handle Deadlocks 如何减少和处理死锁 这个章节建立关于死锁的概念信息,它解释如何组织数据库操作来减少死锁和随后的错误处理: D ...
- 如何设置IIS程序池的回收时间,才能最大程度的减少对用户的影响?
作为.Net开发人员,其实对IIS的应用程序池知之甚少,在工作中我也有几次遇到过网站无故打不开的情况,找了半天原因也找不到是怎么造成的,有一次我给网站找了一个程序程序池后发现就能正常访问了,这也让我对 ...
- 【如何设置IIS程序池的回收时间,才能最大程度的减少对用户的影响?】
作为.Net开发人员,其实对IIS的应用程序池知之甚少,前段时间被问到一个问题: 对于互联网web应用,如何在用户毫无感知的情况下回收程序池?(对用户产生最小的影响) 简单理解IIS应用程序池 应用程 ...
- IoT设备上的恶意软件——通过漏洞、弱密码渗透
2018年,是 IoT 高速发展的一年,从空调到电灯,从打印机到智能电视,从路由器到监控摄像头统统都开始上网.随着5G网络的发展,我们身边的 IoT 设备会越来越多.与此同时,IoT 的安全问题也慢慢 ...
随机推荐
- LeetCode 733: 图像渲染 flood-fill
题目: 有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间. An image is represented by a 2-D array of int ...
- 【shell脚本】通过位置变量创建Linux账户及密码===addUser.sh
通过位置变量创建Linux账户及密码 脚本内容 [root@VM_0_10_centos shellScript]# vi addUser.sh #!/bin/bash # 通过位置变量创建系统账户及 ...
- CSS3特效之转化(transform)和过渡(transition)
CSS3特效之转化(transform)和过渡(transition) 在对动画深入之前,我们需要先了解它的一些特性,CSS3的转化(transform)和过渡(transition).有人可能会有疑 ...
- HTTP Protocol
HTTP协议 1 HTTP请求状态码 当用户试图通过 HTTP 访问一台正在运行 Internet 信息服务 (IIS) 的服务器上的内容时,IIS 返回一个表示该请求的状态的数字代码.状态 ...
- MySQL整形手工注入
0x1 判断注入点: http://www.xxx.org/members.php?id=1 and 1=1 --+ # ture http://www.xxx.org/members.php?id= ...
- PHP开发之--安装composer
composer是一个很好用的工具,类似于iOS开发项目中所用的cocopods,所以就想着安装下,过程也很简单: 1.切换到如下目录(在很多博客中都是这样写的,先安装,然后mv到/usr/local ...
- youtube视频在线下载
youtube视频在线下载网站: https://www.clipconverter.cc/ youtube视频样例: https://www.youtube.com/watch?v=NMkgz0AR ...
- grep日志去重
grep --text ' ERROR '2017.06.08.log | grep '12345678' | grep -Eo 'telephone=.*{11},p'| sort | uniq | ...
- 【PHP】关于系统性能追踪工具molten
一.简介 关于molten的介绍网上有很多,是一个全链路追踪的工具,Molten可以看做是phptrace的的升级版(流行的php问题定位工具譬如phptrace,xhprof,这些工具可以自行Goo ...
- oracle查询中会使索引无效的情况总结
总结一下oracle中会使索引无效的情况 1 无where条件: 2 索引列进行运算时: 3 使用like,并且通配符在前的情况: 4 字符型字段为数字时在where条件里不添加引号: 5 not i ...