1. 程式人生 > 其它 >【Leetcode】928. Minimize Malware Spread II

【Leetcode】928. Minimize Malware Spread II

技術標籤:LC DFS、BFS與圖論leetcode圖論dfs雜湊表資料結構

題目地址:

https://leetcode.com/problems/minimize-malware-spread-ii/

給定一個 n n n個節點的無向圖,以鄰接矩陣給出。每個節點代表一個網路中的節點。再給定一個數組 A A A A [ i ] A[i] A[i]表示節點 i i i被感染了(下文稱 A A A裡的點是”感染源頭“)。當一個節點被感染了,它所在的連通塊的所有節點都會被感染。現在允許選取一個“感染源頭”,將其刪掉(刪掉之後,這個感染源和其所有鄰接邊都會被刪除)。問將哪個節點刪掉可以使得剩餘節點的總的被感染節點數量最小。如果有多個答案,則返回編號最小的節點。

這個問題可以和https://blog.csdn.net/qq_46105170/article/details/113488695一起看,兩者解答可以互相借鑑。DFS做法參考https://blog.csdn.net/qq_46105170/article/details/113491096。下面介紹並查集做法。

我們主要是要考慮刪掉某個感染源後,能“拯救”多少個節點。我們可以先不考慮感染源,然後用並查集統計連通塊。接著,我們用雜湊表統計一下每個連通塊被多少個感染源感染了,略過沒有被感染的連通塊,如果其被多於 1 1 1個感染源感染了,那麼無論刪掉哪個感染源該連通塊都無法被拯救,這樣的連通塊就不考慮了;如果其恰好是被 1 1

1個感染源感染了,那麼將這個感染源刪掉能拯救的節點數,就是這個連通塊元素個數。我們列舉每個感染源,然後看刪去它能拯救多少節點,找到能拯救最多節點的那個感染源即可。如果答案不唯一則找編號最小的那個。程式碼如下:

import java.util.*;

public class Solution {
    
    class UnionFind {
        int[] parent, size;
        
        public UnionFind(int size) {
            parent = new int[size];
            for
(int i = 0; i < size; i++) { parent[i] = i; } this.size = new int[size]; Arrays.fill(this.size, 1); } public int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } public void union(int x, int y) { int px = find(x), py = find(y); if (px == py) { return; } parent[px] = py; size[py] += size[px]; } } public int minMalwareSpread(int[][] graph, int[] initial) { int n = graph.length; // 記錄一下哪些點是感染源 boolean[] dirty = new boolean[n]; for (int x : initial) { dirty[x] = true; } // 先不考慮感染源,求一下連通塊 UnionFind uf = new UnionFind(n); for (int i = 0; i < n; i++) { if (!dirty[i]) { for (int j = i + 1; j < n; j++) { if (!dirty[j] && graph[i][j] == 1) { uf.union(i, j); } } } } // 求一下每個連通塊被哪些感染源感染了,key存連通塊的樹根,value存能感染到該連通塊的感染源編號 Map<Integer, Set<Integer>> infectorMap = new HashMap<>(); for (int x : initial) { for (int y = 0; y < graph[x].length; y++) { // 如果通過x能感染到y,則用雜湊表存一下 if (!dirty[y] && graph[x][y] == 1) { int py = uf.find(y); infectorMap.putIfAbsent(py, new HashSet<>()); infectorMap.get(py).add(x); } } } // 求一下每個感染源如果將其刪掉,能拯救多少個節點。 // 注意這裡要略過被多個感染源感染的連通塊,這些連通塊是無法被拯救的 Map<Integer, Integer> saveCount = new HashMap<>(); for (Map.Entry<Integer, Set<Integer>> entry : infectorMap.entrySet()) { int parent = entry.getKey(); Set<Integer> infectors = entry.getValue(); // 如果當前連通塊被多個感染源感染了,那就略過 if (infectors.size() > 1) { continue; } // 累加一下這個感染源如果刪掉能拯救多少個節點 int infector = infectors.iterator().next(); saveCount.put(infector, saveCount.getOrDefault(infector, 0) + uf.size[parent]); } // 如果每個連通塊都被多於1個感染源感染了,那就刪掉編號最小的感染源 if (saveCount.isEmpty()) { int res = n; for (int i : initial) { res = Math.min(res, i); } return res; } // 否則去找一下被拯救節點最多的那個感染源 int res = -1, maxSave = 0; for (Map.Entry<Integer, Integer> entry : saveCount.entrySet()) { int infector = entry.getKey(), saved = entry.getValue(); if (saved > maxSave) { maxSave = saved; res = infector; } else if (saved == maxSave) { res = Math.min(res, infector); } } return res; } }

時間複雜度 O ( n 2 log ⁡ ∗ n ) O(n^2\log ^* n) O(n2logn),空間 O ( n ) O(n) O(n)