You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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];
}
}
}
}
}