[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 的安全问题也慢慢 ...
随机推荐
- mysqldump导表
mysqldump全量导表 mysqldump -hlocalhost -uroot -P3306 -p --skip-add-locks --skip-triggers test > test ...
- 【前端开发环境】前端使用GIT管理代码仓库需要掌握的几个必备技巧和知识点总结
1. Git的三种状态 已提交 committed 已暂存 staged 已修改 modified 2. Git的三个区域 Git仓库 是 Git 用来保存项目的元数据和对象数据库的地方. 这是 Gi ...
- Element-ui 下拉列表 全选 多选时 select全选 新增一个选择所有的选项
项目里经常会用到,在一个多选下拉框里新增一个选择所有的选项,例如: <!DOCTYPE html> <html lang="en"> <head> ...
- 七道常见的Redis面试题分享(含个人解答)
绝大部分写业务的程序员,在实际开发中使用 Redis 的时候,只会 Set Value 和 Get Value 两个操作,对 Redis 整体缺乏一个认知.这里以面试题的形式对 Redis 常见问题做 ...
- FIddler+Proxifer 实现PC客户端抓包
Proxifier设置 1.安装Proxifier 下载地址:http://www.hanzify.org/software/13717.html 2.配置代理服务器 A.开启HTTP协议 Proxi ...
- python操作时间
一.问题背景 在对数据进行操作的时候我们总是会遇到数据类型是date类型的数据,这种数据会让我们在使用和操作的过程中遇到一些问题,比如int类型和date类型不对等,string类型和date类型不对 ...
- 基于Spark的电影推荐系统(推荐系统~1)
第四部分-推荐系统-项目介绍 行业背景: 快速:Apache Spark以内存计算为核心 通用 :一站式解决各个问题,ADHOC SQL查询,流计算,数据挖掘,图计算 完整的生态圈 只要掌握Spark ...
- GSOAP服务卡住?
很久以前参考了https://www.genivia.com/doc/soapdoc2.html 中的一段: How to Create a Multi-Threaded Stand-Alone Se ...
- X264-视频帧的存取
X264的编码器结构体x264_t中的子结构体字段frames包含了4个临时视频帧序列空间:current.next.unused和reference,分别保存当前编码帧.将编码帧序列.未处理原始视频 ...
- SUSE12-SP2安装教程(虚拟机)
创建虚拟机,安装系统,安装系统后的系统设置 创建虚拟机 将SUSE12-SP2镜像(大于3G)上传到虚拟机主机存储. 创建虚拟机创建虚拟机,CPU>=8核,内存>=16G(注:我这里仅演示 ...