不会用Union-FInd 做 928. Minimize Malware Spread II

这是 本人 leetcode 924. Minimize Malware Spread的Union-FInd的解法。想在此基础上改出 928. Minimize Malware Spread II, 各种不成功,求大神指点下啊。

/**
 * @param {number[][]} graph
 * @param {number[]} initial
 * @return {number}
 */
var minMalwareSpread = function(graph, initial) {
    const n = graph.length;
    const roots = Array(n);
    for (let i = 0; i < n; i++) roots[i] = i;
    
    for (let i = 0; i < n; i++) {
        for (let j = i+1; j < n; j++) {
            if (graph[i][j]) union(i, j);
        }
    }

    const ufSize = Array(n).fill(0);
    const malCount = Array(n).fill(0);
    for (let i = 0; i < n; i++) ufSize[find(i)]++;  //统计每个连接区域的大小
    for (let i of initial) malCount[find(i)]++;     //统计每个被感染的连接区域的大小

    let res = -1;
    let maxSize = 0;
    initial.sort((a,b) => a-b);     //排序是因为返回index尽量小的受感染节点
    for (let init of initial) {
        let idx = find(init);
        if (malCount[idx] === 1 && ufSize[idx] > maxSize) {   // 每个受感染的联通子图里受感染的节点是1个,给他恢复才能阻止其他节点受感染,并且当前联通区域面积最大,才能拯救更多的节点不受感染
            maxSize = ufSize[idx];
            res = init;
        }
    }

    return res === -1 ? initial[0] : res; //受感染的联通子图里多余两个受感染节点,返回index小的受感染节点
    
    function find(x) {
        while (x !== roots[x]) {
            x = roots[x];
            roots[x] = roots[roots[x]];
        }
        return x;
    }
    
    function union(x, y) {
        if (x !== y) roots[find(x)] = find(y);
    }
}

这两题都是dropbox的哈
924的是把感染的某个节点恢复,928是把感染的某个节点连同他的边删除

leetcode 924 Minimize Malware Spread 解题笔记

13 October 2018

题目

leetcode 924, Minimize Malware Spread

  1. 给定一个用图表示的网络,如果graph[i][j] = 1, 说明节点i和j之间有连接
  2. 如果一个节点被病毒感染,所有和这个节点有连接的节点都会被病毒感染,并且会不断扩散
  3. 输入一个初始节点数组,表示已经感染病毒的节点

问题,假如从初始节点中删除一个节点,选择哪个节点会使得最后已感染病毒的节点最少?

分析

  1. 这个题目最正规的做法应该是union find, 不过因为之前写了一个错误的bfs版本, 所以这里在原来错误bfs的版本上修正了一下。
  2. 这里修正的情况是针对有两个initial节点可以互相连接的情况,也就是remove其中一个不会改变最后被感染的机器个数, 所以简单的加了一个set判断了一下
  3. 很可惜, leetcode的test case没有很好的覆盖这类情况, 原来的错误代码也可以ac, 所以我把这个case提交给leetcode了
    比如对下面这个图,
    图的矩阵表示: [[1,1,0,0,0],[1,1,1,0,0],[0,1,1,0,0],[0,0,0,1,1],[0,0,0,1,1]]
    initial nodes分别是: [0,1,3]
0--1--2

3--4

虽然0和1都可以感染3个节点,但是无论删除0还是1都不会减少被感染的node, 反而remove 3可以减少被感染的node。
所以这个case应该返回3而不是0

Java代码

public int minMalwareSpread(int[][] graph, int[] initial) {
    Map<Integer, Integer> countMap = new HashMap<>();
    Set<Integer> set = new HashSet<>();
    for(int v: initial) {
        set.add(v);
    }
    
    for(int v: initial) {
        if(!countMap.containsKey(v)) {
            countMap.putAll(calcImpacted(graph, v, set));                                
        }
    }
    
    int maxCount = -1, retv = 0;
    for(Map.Entry<Integer, Integer> entry: countMap.entrySet()) {
        if(!set.contains(entry.getKey())) continue;
        if(entry.getValue() > maxCount) {
            retv = entry.getKey();
            maxCount = entry.getValue();
        }
        else if(entry.getValue() == maxCount && entry.getKey() < retv) {
            retv = entry.getKey();
        }
    }
    return retv;
}

public Map<Integer, Integer> calcImpacted(int[][] graph, int initial, Set<Integer> initialSet) {
    Queue<Integer> queue = new LinkedList<>();
    queue.add(initial);
    Set<Integer> set = new HashSet<Integer>();      
    boolean dup = false;
    while(queue.size() > 0) {
        int v = queue.poll();
        set.add(v);
        for(int j = 0; j < graph.length; ++j) {
            if(graph[v][j] == 1 && !set.contains(j)) {
                queue.add(j);
                if(j != initial && initialSet.contains(j)) {
                    dup = true;
                }
            }
        }
    }
    Map<Integer, Integer> countMap = new HashMap<Integer,Integer>();
    for(int v: set) {
        int count = dup ? 0 : set.size();
        countMap.put(v, count);
    }
    return countMap;
    
}

http://www.noteanddata.com/leetcode-928-Minimize-Malware-Spread-II-java-solution-note-2.html

笔记和数据

Leetcode 928. Minimize Malware Spread II 非brute force版本解题笔记

21 October 2018

题目

leetcode 928. Minimize Malware Spread II

leetcode 928 Minimize Malware Spread II解法分析

  1. 这个题目之前写了一个brute force的版本,可以ac,但是复杂度比较高,效率不是很好。
    http://www.noteanddata.com/leetcode-928-Minimize-Malware-Spread-II-solution-note.html
    现在写了一个非brute force的版本。
  2. 这里主要的区别是重复的部分, 也就是两个initial会连到一起,然后污染同一批机器
    假如有如下图, 然后initial是[1,5]
    如果删除1, 就是剩下5,被感染的机器是[2,3,4,5,6]
    如果删除5, 就是剩下1,被感染的机器是[1,2,3,4]
1---2---3---4
    |
    |
    5---6

从这个例子可以看出,有一些节点可能会被两个initial都感染到,而有一些节点只能被某一些initial感染到。
如果移除某个initial并且把相关的连接都切断以后, 那么那些只能被这个initial感染的节点就不能被感染, 这实际上就是真正被移除的节点。

  1. 所以, 可以bfs访问每个initial节点,然后看只能被这个节点感染的节点有哪些。
    a. 如果在bfs的过程中遇到其他initial节点就不访问
    b. 如果任意某一个节点被一个initial节点访问到过,那就不算是“只能被某个initial节点感染的节点”, 应该从记录中删除

Java代码

public int minMalwareSpread(int[][] graph, int[] initial) {
    Set<Integer> initialSet = new HashSet<>();
    for(int node: initial) {
        initialSet.add(node);
    }
    Map<Integer,Integer> uniqueSourceMap = new HashMap<>();
    Set<Integer> uniqueSourceSet = new HashSet<>();
    for(int node: initial) {
        Set<Integer> visited = new HashSet<>();
        Queue<Integer> queue = new LinkedList<>();
        queue.add(node);
        while(queue.size() > 0) {
            int v = queue.poll();
            visited.add(v);

            if(!uniqueSourceSet.contains(v)) {
                uniqueSourceSet.add(v);                    
                uniqueSourceMap.put(v, node);
            }
            else {
                uniqueSourceMap.remove(v);
            }

            for(int i = 0; i < graph.length; ++i) {
                if(graph[v][i] == 1 && !initialSet.contains(i) && !visited.contains(i)) {
                    queue.add(i);                        
                }
            }
        }
    }
    int[] countTable = new int[graph.length];
    int maxi = 0;
    
    for(Map.Entry<Integer, Integer> entry: uniqueSourceMap.entrySet()) {
        int i = entry.getValue();
        countTable[i]++;            
        if(countTable[i] > countTable[maxi]) maxi = i;
        else if(countTable[i] == countTable[maxi] && i < maxi) maxi = i;            
    }
    return maxi;
}

时间复杂度

所有操作是O(M+M N N+N), 也就是O(M N N), 其中M是initial的个数, N是graph的节点数

空间复杂度

O(M+N*2+N), 也就是O(N)

其他思考

写完这个以后, 感觉好像之前的924虽然可以ac但是似乎做错了?,http://www.noteanddata.com/leetcode-924-Minimize-Malware-Spread.html
对于重复情况的判断似乎没有考虑, 那个正确的做法应该是用union find来做, 后面准备验证一下。
或者如果bfs的话还是需要每次把一个节点移除然后再做处理, 直接计数应该不对的。

II是从被感染的节点出发,找只被一个感染节点访问过的,这个不适合用union find做。II和I解题思路完全不一样,I确实适合uf做,博主也分析了这点。感觉没必要刻意追求II也用uf做。两题虽然看似像,但解法完全思路不一样,没必要刻意追求一致性,那是deadend