[LeetCode] 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

這道題是之前那道 Minimize Malware Spread 的拓展,實際上只是修改了一個地方,就是文中加粗的地方,說的是每次直接將這個結點去掉,而不是像之前的題目那樣只是將其不當做感染源。那么二者到底有什么區別呢,實際上並不是在所有的 test case 上都有區別,只是部分會有。比如對於 graph=[[1,1,0],[1,1,1],[0,1,1]], initial=[0,1] 來說,可以發現結點的鏈接情況是 0-1-2,感染源結點是0和1,若是按之前題目的要求,移除0和1都不會減少最終感染個數,但是應該返回結點0,因為其 index 小。但是應用此題的條件,就一定要返回結點1,因為移除結點1之后,就斷開了結點0和結點2的連接,最終只有病毒源結點0會保持感染狀態,這就是二者的區別所在。解題思路完全可以在之前那道題 Minimize Malware Spread 的基礎上進行修改,最簡單暴力的修改其實是新建一個 graph 的副本,然后當要刪掉某個結點i的時候,將所有的 graph[i][:] 和 graph[:][i] 都賦值為0即可,這樣修改鄰接矩陣就相當於斷開了結點i和其他所有結點之間的連接,博主親測解法1和解法2都可以用此方法通過。當然也可以用其他的方法,這里博主將要刪除的結點 num 帶入到了子函數中,在 BFS 中遍歷某個結點的所有鄰接點的時候,只要多加個判斷,跳過 num 結點也是可以的,參見代碼如下:


解法一:

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, num);
            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, int num) {
        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 (i == num || graph[t][i] != 1 || infected.count(i)) continue;
                infected.insert(i);
                q.push(i);
            }
        }
        return infected.size();
    }
};

當然對於 DFS 的解法也可以做類似的處理,除了上面提到的建立 graph 副本並修改的方法之外,也可以用其他的方法。這里博主對於每個要移除的結點 num,先將其加入 visited 之中,然后在遍歷的時候,只對非 num 結點調用遞歸函數,可以得到同樣的結果,參見代碼如下:


解法二:

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{{num}};
            for (int cur : infected) {
                if (cur != num) 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);
        }
    }
};

討論:博主本想將之前那道 Minimize Malware Spread 的解法三也修改一下用到這里,但發現比較難修改,論壇雖然也有一些使用聯合查找 Union Find 來做的解法,但是感覺都比較復雜,這里就不寫了,若哪位看官大神可以將之前的解法三移植過來,請務必留言告訴博主哈~


Github 同步地址:

https://github.com/grandyang/leetcode/issues/928


類似題目:

Minimize Malware Spread


參考資料:

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

https://leetcode.com/problems/minimize-malware-spread-ii/discuss/217529/c%2B%2B-solution-bfs

https://leetcode.com/problems/minimize-malware-spread-ii/discuss/184645/Straightforward-DFS-Java-6-ms


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM