LeetCode 928. 尽量减少恶意软件的传播 II

本文介绍了一个算法,用于在一个给定的图中,针对初始感染的节点,找到最少需要删除的节点,以使得连通分量内的恶意软件传播降到最低。算法利用了连通性分析、DFS和计算环中的节点数量来确定最优策略。
摘要由CSDN通过智能技术生成
class Solution {
public:
    vector<int> fa;     // 每个连通分量的根节点
    vector<int> countPerson;    // 每个联通分量的总人数
    vector<int> countInitial;   // 每个联通分量内init感染数
    vector<int> isInitial;      // 记录initial的下标
    vector<vector<int>> graph;
    int minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial) {
        int n = graph.size();
        fa = vector<int>(n);
        countPerson = vector<int>(n, 1);
        countInitial = vector<int>(n, 0);
        isInitial = vector<int>(n, 0);
        this->graph = graph;

        for(int i = 0; i < n; ++i) {
            fa[i] = i;
        }
        for(int i = 0;i < initial.size(); ++i) {
            isInitial[initial[i]] = 1;
            countInitial[initial[i]] = 1;
        }

        for(int i = 0; i < n; ++i) {
            for(int j = 0;j < n; ++j) {
                if(graph[i][j]) merge(i,j);
            }
        }

        int ret = -1;int maxRemovePerson = 0;
        for(int i = 0;i < initial.size(); ++i) {
            int f = find(initial[i]);
            int removePerson = countPerson[f];
            // 若连通分量内有超过1个的initial节点,则需要取最优
            if(countInitial[f] > 1) {
                removePerson = getRemovePerson(initial[i]) + 1;
            }

            if(maxRemovePerson < removePerson) {
                ret = initial[i];
                maxRemovePerson = removePerson;
            } else if(maxRemovePerson == removePerson && initial[i] < ret) {
                ret = initial[i];
            }
        }
        return ret;
    }

    int find(int x) {
        if(x == fa[x]) return x;
        return fa[x] = find(fa[x]);
    }

    void merge(int x,int y) {
        int fa_x = find(x);
        int fa_y = find(y);
        if(fa_x != fa_y) {
            fa[fa_y] = fa_x;
            countPerson[fa_x] += countPerson[fa_y];
            countInitial[fa_x] += countInitial[fa_y];
        }
    }

    // 获取到去除x结点后避免被感染的结点数量
    int getRemovePerson(int x) {
        int n = graph.size();
        bool isLoop = findLoop(x);  // 查询当前根节点是否在环中
        // 若当前节点在环中,则表示即使去除当前结点,整个连通分量还是会被全部感染
        if(isLoop) return 0;        // 是,则直接退出

        // 若不是,则以当前结点为根节点,查找图的结点数量
        int count = 0;              // 记录结点数
        vector<int> vis(n, 0);      // 记录结点是否已被访问
        vis[x] = 1;
        for(int i = 0;i < n; ++i) {
            if(i != x && graph[x][i] && !vis[i]) count += countChildPerson(i, vis);
        }
        return count;
    }


    int countChildPerson(int x, vector<int>& vis) {
        queue<int> q;
        q.push(x);
        vis[x] = 1;
        int count = 0;
        bool status = false;    // 记录路径中是否有被感染的结点
        while(!q.empty()) {
            auto cur = q.front();
            q.pop();
            if(isInitial[cur]) status = true;   // cur为感染结点
            count++;
            for(int i = 0; i < graph.size(); ++i) {
                if(graph[cur][i] && !vis[i]) {
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
        if(status) return 0;    // 若路径中存在感染结点,则整条路径都会被感染
        return count;
    }

    // 判断x是否为循环结点
    bool findLoop(int x) {
        int n = graph.size();
        vector<int> vis(n, 0);
        bool isHasOutNode = false; // 判断当前结点是否有超过1个的外接结点
        bool status = false;

        vis[x] = 1;

        for(int i = 0; i < n; ++i) {
            if(i == x) continue;
            if(graph[x][i] && !vis[i]) {
                if(isHasOutNode) return false;  // 若有外接结点,则非环
                isHasOutNode = true;
                vis[i] = 1;
                status = dfs(x, x, i, vis); // dfs查询x是否在环中
            }
        }
        return status;
    }

    bool dfs(int target, int fa, int x, vector<int>& vis) {

        for(int i = 0; i < graph.size(); ++i) {
            if(i == x || i == fa) continue; // 若为当前结点或是父节点则跳过
            if(graph[x][i]) {
                if(vis[i] && i == target) return true; // 若找到target,则表示target在环中
                if(!vis[i]) {
                    vis[i] = 1;
                    bool status = dfs(target, x, i, vis);
                    if(status) return true;
                }
            }
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值