【每日一题】尽量减少恶意软件的传播

924. 尽量减少恶意软件的传播

关键词:并查集、深度优先

题目来源:924. 尽量减少恶意软件的传播 - 力扣(Leetcode)

题目描述
T并查集
T深度优先

给出了一个由 n 个节点组成的网络,用 n × n 个邻接矩阵图 graph 表示。在节点网络中,当 graph[i][j] = 1 时,表示节点 i 能够直接连接到另一个节点 j

一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。

假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。

如果从 initial移除某一节点能够最小化 M(initial), 返回该节点。如果有多个节点满足条件,就返回编号最小的节点。

请注意,如果某个节点已从受感染节点的列表 initial 中删除,它以后仍有可能因恶意软件传播而受到感染。

输入:graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
输出:0
输入:graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
输出:0
输入:graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
输出:1
数据描述
n == graph.length
n == graph[i].length
2 <= n <= 300
graph[i][j] == 0 或 1
graph[i][j] == graph[j][i]
graph[i][i] == 1
1 <= initial.length <= n
0 <= initial[i] <= n - 1
initial` 中所有整数均不重复
问题分析

初步印象:图、连通块、并查集/深度优先

对于一个连通块,只要连通块内有结点被感染,整个连通块便会被感染。

根据题意,只能移除一个结点,当连通块内有2个及以上结点被感染时,不管移除哪个结点,被移除的结点仍会被在同一连通块内的其它被感染结点感染,从而整体被感染结点数并不会减少。

对于那些只有1个被感染结点的连通块,移除唯一被感染结点后,整体被感染的结点减少的数量就是该连通块中的结点数,于是,要想整体被感染的结点数最少,只需要找到“只有1个被感染结点的连通块”中含结点数最多的连通块即可。

经过上述分析,问题就转变为:找到只有1个被感染结点的连通块中含结点数最多的连通块。

当没有只有1个被感染结点的连通块时,整体被感染结点数并不会减少,依题意,返回被感染结点中编号最小的结点即可。

维护连通块可以使用并查集,也可以使用深搜给每个点打上标记。

时间复杂度:O(n^2)

空间复杂度:O(n)

代码实现
int minMalwareSpread(vector<vector<int>> &graph, vector<int> &initial) {

    int n = graph.size();

    // 构建并查集
    // 1.并查数组
    int p[n], size[n];
    // 2.模板函数
    function<int(int)> find = [&](int x) {
        if (p[x] != x)p[x] = find(p[x]);
        return p[x];
    };
    function<void(int, int)> unite = [&](int a, int b) {
        a = find(a), b = find(b);
        if (a == b)return;
        if (size[a] < size[b])swap(a, b);
        p[b] = a, size[a] += size[b];
    };
    // 3.初始化
    for (int i = 0; i < n; i++) {
        p[i] = i, size[i] = 1;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++) {
            if (graph[i][j])unite(i, j);
        }
    }

    // initial数组去重
    int cnt[n];
    memset(cnt, 0, sizeof cnt);
    for (int e: initial)cnt[find(e)]++;   // 被感染且唯一的点的对应的cnt值为1

    // 找到所在连通块点数最大的点(唯一点)
    int curSize = 0, idx = -1;
    for (int e: initial) {
        int rt = find(e);
        if (cnt[rt] == 1) {         // 被感染且唯一的点
            if (curSize < size[rt])curSize = size[rt], idx = e;
            else if (curSize == size[rt] && e < idx)idx = e;
        }
    }

    // 没有唯一点
    if (idx == -1) {
        idx = INT_MAX;
        for (int e: initial)
            if (e < idx)idx = e;
    }

    return idx;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值