|
|
package class_2023_02_2_week;
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
// 给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示
|
|
|
// 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j。
|
|
|
// 一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,
|
|
|
// 且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。
|
|
|
// 这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。
|
|
|
// 假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。
|
|
|
// 我们可以从 initial 中删除一个节点,
|
|
|
// 并完全移除该节点以及从该节点到任何其他节点的任何连接。
|
|
|
// 请返回移除后能够使 M(initial) 最小化的节点。
|
|
|
// 如果有多个节点满足条件,返回索引 最小的节点 。
|
|
|
// initial 中每个整数都不同
|
|
|
// 测试链接 : https://leetcode.cn/problems/minimize-malware-spread-ii/
|
|
|
public class Code04_MinimizeMalwareSpreadII {
|
|
|
|
|
|
public int minMalwareSpread(int[][] graph, int[] initial) {
|
|
|
// 节点个数
|
|
|
int n = graph.length;
|
|
|
// 病毒数组,
|
|
|
// 病毒initial -> [3,6,9]
|
|
|
// virus[3] = true;
|
|
|
// virus[6] = true;
|
|
|
// virus[7] = false;
|
|
|
boolean[] virus = new boolean[n];
|
|
|
for (int i : initial) {
|
|
|
virus[i] = true;
|
|
|
}
|
|
|
// 建立并查集
|
|
|
// n大小 0~n-1
|
|
|
UnionFind uf = new UnionFind(n);
|
|
|
// 忽略感染点及其链接!
|
|
|
// 合并集合!
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
for (int j = 0; j < n; j++) {
|
|
|
// i, j ?
|
|
|
if (graph[i][j] == 1 && !virus[i] && !virus[j]) {
|
|
|
uf.union(i, j);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// 所有非感染点,该链接都链接了!
|
|
|
// 0,6,7,8 是一个集合
|
|
|
// 6被选成了代表点,无法控制,只由并查集自己决定
|
|
|
// infect[6] == -1 ,目前这个集合没有感染源
|
|
|
// infect[6] == -2 ,目前这个集合已经发现了不只一个感染源
|
|
|
// infect[6] == x x>=0,目前这个集合已经发现了一个感染源,是x点
|
|
|
int[] infect = new int[n];
|
|
|
// 目前所有集合都没有感染源
|
|
|
Arrays.fill(infect, -1);
|
|
|
for (int v : initial) { // 枚举所有感染点!
|
|
|
// 当前感染点是v
|
|
|
// 跟v链接的所有点,next
|
|
|
// n = 7 编号0~6
|
|
|
// n = 13 编号0~12
|
|
|
for (int next = 0; next < n; next++) {
|
|
|
if (v != next && !virus[next] && graph[v][next] == 1) {
|
|
|
// next 无辜点
|
|
|
// 找到next集合所在的代表点,f
|
|
|
int f = uf.find(next);
|
|
|
if (infect[f] == -1) {
|
|
|
infect[f] = v;
|
|
|
} else { // != -1 可能 == -2 或者 >= 0
|
|
|
// 之前的感染点 -> infect[f]
|
|
|
if (infect[f] != -2 && infect[f] != v) {
|
|
|
infect[f] = -2;
|
|
|
}
|
|
|
// infect[f] == -2
|
|
|
// infect[f] == v
|
|
|
// 这两种情况,一律不设置是符合预期的
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
int[] cnt = new int[n];
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
if (infect[i] >= 0) {
|
|
|
cnt[infect[i]] += uf.size[i];
|
|
|
}
|
|
|
}
|
|
|
// 4 -> 13
|
|
|
// 0 -> 6
|
|
|
// 1 -> 5
|
|
|
// 13 -> 50
|
|
|
// 9 -> 20
|
|
|
// 0 1 4 9 13 14 15
|
|
|
// 6 5 13 20 50 34 7
|
|
|
Arrays.sort(initial);
|
|
|
int ans = initial[0];
|
|
|
int count = cnt[ans];
|
|
|
for (int i : initial) {
|
|
|
if (cnt[i] > count) {
|
|
|
ans = i;
|
|
|
count = cnt[i];
|
|
|
}
|
|
|
}
|
|
|
return ans;
|
|
|
}
|
|
|
|
|
|
public static class UnionFind {
|
|
|
public int[] father;
|
|
|
public int[] size;
|
|
|
public int[] help;
|
|
|
|
|
|
public UnionFind(int n) {
|
|
|
father = new int[n];
|
|
|
size = new int[n];
|
|
|
help = new int[n];
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
father[i] = i;
|
|
|
size[i] = 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public int find(int i) {
|
|
|
int hi = 0;
|
|
|
while (i != father[i]) {
|
|
|
help[hi++] = i;
|
|
|
i = father[i];
|
|
|
}
|
|
|
while (hi != 0) {
|
|
|
father[help[--hi]] = i;
|
|
|
}
|
|
|
return i;
|
|
|
}
|
|
|
|
|
|
public void union(int i, int j) {
|
|
|
int fi = find(i);
|
|
|
int fj = find(j);
|
|
|
if (fi != fj) {
|
|
|
if (size[fi] >= size[fj]) {
|
|
|
father[fj] = fi;
|
|
|
size[fi] += size[fj];
|
|
|
} else {
|
|
|
father[fi] = fj;
|
|
|
size[fj] += size[fi];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|