0928. Minimize Malware Spread I I

# 928. Minimize Malware Spread II#

## 题目 #

(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`

## 题目大意 #

(这个问题与 尽量减少恶意软件的传播 是一样的，不同之处用粗体表示。)在节点网络中，只有当 graph[i][j] = 1 时，每个节点 i 能够直接连接到另一个节点 j。一些节点 initial 最初被恶意软件感染。只要两个节点直接连接，且其中至少一个节点受到恶意软件的感染，那么两个节点都将被恶意软件感染。这种恶意软件的传播将继续，直到没有更多的节点可以被这种方式感染。假设 M(initial) 是在恶意软件停止传播之后，整个网络中感染恶意软件的最终节点数。我们可以从初始列表中删除一个节点，并完全移除该节点以及从该节点到任何其他节点的任何连接。如果移除这一节点将最小化 M(initial)， 则返回该节点。如果有多个节点满足条件，就返回索引最小的节点。

• 1 < graph.length = graph[0].length <= 300
• 0 <= graph[i][j] == graph[j][i] <= 1
• graph[i][i] = 1
• 1 <= initial.length < graph.length
• 0 <= initial[i] < graph.length

## 解题思路 #

• 这一题是第 924 题的加强版。给出一个节点之间的关系图，如果两个节点是连通的，那么病毒软件就会感染到连通的所有节点。现在如果想完全彻底移除一个病毒节点，能最大减少感染，请问移除哪个节点？如果多个节点都能减少感染量，优先移除序号偏小的那个节点。这一题的输入输出要求和第 924 题是完全一样的，区别在于第 924 题实际上是要求把一个病毒节点变成非病毒节点，而这道题是完全删除一个病毒节点以及它连接的所有边。
• 这一题考察的是并查集。当然用 DFS 也可以解答这一题。并查集的做法如下，首先先将所有的病毒节点去掉，然后将所有连通块合并成一个节点。因为一个连通集合中的节点，要么全部被感染，要么全部不被感染，所以可以把每个集合整体考虑。然后统计所有集合直接相邻的病毒节点的个数。对于一个集合来说：
1. 如果直接相邻的病毒节点的个数为 0，则一定不会被感染，忽略这种情况；
2. 如果直接相邻的病毒节点的个数为 1，则将该病毒节点删除后，整个连通块就可以避免被感染，这种情况是我们寻找的答案；
3. 如果直接相邻的病毒节点的个数大于等于2，则不管删除哪个病毒节点，该连通块都仍会被感染，忽略这种情况；
• 所以只需在所有第二种连通块（直接相邻的病毒节点的个数为 1 的连通块）中，找出节点个数最多的连通块，与它相邻的病毒节点就是我们要删除的节点；如果有多个连通块节点个数相同，再找出与之对应的编号最小的病毒节点即可。

## 代码 #

``````
package leetcode

import (
"math"

"github.com/halfrost/leetcode-go/template"
)

func minMalwareSpread2(graph [][]int, initial []int) int {
if len(initial) == 0 {
return 0
}
uf, minIndex, count, countMap, malwareMap, infectMap := template.UnionFind{}, initial[0], math.MinInt64, map[int]int{}, map[int]int{}, map[int]map[int]int{}
for _, v := range initial {
malwareMap[v]++
}
uf.Init(len(graph))
for i := range graph {
for j := range graph[i] {
if i == j {
break
}
if graph[i][j] == 1 && malwareMap[i] == 0 && malwareMap[j] == 0 {
uf.Union(i, j)
}
}
}
for i := 0; i < len(graph); i++ {
countMap[uf.Find(i)]++
}
// 记录每个集合和直接相邻病毒节点的个数
for _, i := range initial {
for j := 0; j < len(graph); j++ {
if malwareMap[j] == 0 && graph[i][j] == 1 {
p := uf.Find(j)
if _, ok := infectMap[p]; ok {
infectMap[p][i] = i
} else {
tmp := map[int]int{}
tmp[i] = i
infectMap[p] = tmp
}
}
}
}
// 选出病毒节点中序号最小的
for _, v := range initial {
minIndex = min(minIndex, v)
}
for i, v := range infectMap {
// 找出只和一个病毒节点相连通的
if len(v) == 1 {
tmp := countMap[uf.Find(i)]
keys := []int{}
for k := range v {
keys = append(keys, k)
}
if count == tmp && minIndex > keys[0] {
minIndex = keys[0]
}
if count < tmp {
minIndex = keys[0]
count = tmp
}
}
}
return minIndex
}

``````

Apr 8, 2023