【八十二】【算法分析与设计】2421. 好路径的数目,928. 尽量减少恶意软件的传播 II,并查集的应用,元素信息绑定下标一起排序,元素通过下标进行绑定

news2024/12/30 0:46:49

2421. 好路径的数目

给你一棵 n 个节点的树(连通无向无环的图),节点编号从 0n - 1 且恰好有 n - 1 条边。

给你一个长度为 n 下标从 0 开始的整数数组 vals ,分别表示每个节点的值。同时给你一个二维整数数组 edges ,其中 edges[i] = [a(i), b(i)] 表示节点 a(i)b(i)( )之间有一条 无向 边。

一条 好路径 需要满足以下条件:

  1. 开始节点和结束节点的值 相同

  2. 开始节点和结束节点中间的所有节点值都 小于等于 开始节点的值(也就是说开始节点的值应该是路径上所有节点的最大值)。

请你返回不同好路径的数目。

注意,一条路径和它反向的路径算作 同一 路径。比方说, 0 -> 11 -> 0 视为同一条路径。单个节点也视为一条合法路径。

示例 1:

输入:vals = [1,3,2,1,3], edges = [[0,1],[0,2],[2,3],[2,4]] 输出:6 解释:总共有 5 条单个节点的好路径。 还有 1 条好路径:1 -> 0 -> 2 -> 4 。 (反方向的路径 4 -> 2 -> 0 -> 1 视为跟 1 -> 0 -> 2 -> 4 一样的路径) 注意 0 -> 2 -> 3 不是一条好路径,因为 vals[2] > vals[0] 。

示例 2:

输入:vals = [1,1,2,2,3], edges = [[0,1],[1,2],[2,3],[2,4]] 输出:7 解释:总共有 5 条单个节点的好路径。 还有 2 条好路径:0 -> 1 和 2 -> 3 。

示例 3:

输入:vals = [1], edges = [] 输出:1 解释:这棵树只有一个节点,所以只有一条好路径。

提示:

  • n == vals.length

  • 1 <= n <= 3 * 10(4)

  • 0 <= vals[i] <= 10(5)

  • edges.length == n - 1

  • edges[i].length == 2

  • 0 <= a(i), b(i) < n

  • a(i) != b(i)

  • edges 表示一棵合法的树。

1.

对于所有的边元素,我们可以得到他max的vals的值,并且可以构造一个max数组一一对应.

max进行排序,从小到大排序,并且是绑定下标进行排序.

这样我们就可以按照元素的max信息顺序进行访问元素本身.

2.

对于每一个边,如果两个点的最大值是不一样的,那么肯定不是好路径.

如果两个点的最大值是一样的,那么好路径的个数是两个点所在集合元素个数相乘,然后这两个结合合并.

 
class Solution {
public:
    vector<int> vals;//每一个点的值
    vector<vector<int>> edges;//每一个边信息
    int ret;//存储结果
    using p = pair<int, int>;//定义pair
    vector<p> max_edge;//为了绑定max和下标一起排序
    int n;//点的个数

    struct node {//存储所有元素对于的其他的信息
        int max;//存储集合最大值信息
        int size;//存储集合最大值个数信息
        int father;//并查集核心部分
    };

    vector<node> vec;//所有元素对应的其他的信息
    vector<int> st;//用vector模拟栈
    int _find(int i) {//并查集中找i下标对应集合的代表元素的下标
        while (!(i == vec[i].father)) {//当前节点是i,出口时i=vec[i].father
            st.push_back(i);//用栈存储走过的路
            i = vec[i].father;//进入下一节点的操作
        }

        while (st.size()) {//将所有走过的路的father直接设置为集合代表元素的下标
        //扁平化处理
            vec[st.back()].father = i;
            st.pop_back();
        }
        return i;
    }
    bool isSameSet(int x, int y) { return _find(x) == _find(y); }//判断是否是同一个集合
    void _union(int x, int y) {//将两个集合进行合并
        if (_find(x) == _find(y))
            return;
        else {
            int rx = _find(x);
            int ry = _find(y);
            int maxx = vec[rx].max;
            int maxy = vec[ry].max;

            if (maxx == maxy)//如果最大值是一样的,那么最大值的个数相加
                vec[ry].size = vec[rx].size + vec[ry].size;
            else if (maxx > maxy)//如果最大值不是一样的,只需要考虑这一种情况,修改ry的size
                vec[ry].size = vec[rx].size;

            vec[ry].max = max(vec[rx].max, vec[ry].max);

            vec[rx].father = vec[ry].father;
        }
    }
    void init() {//正式解题操作之前的初始化操作
        n = edges.size();
        for (int i = 0; i < n; i++) {
            max_edge.push_back({max(vals[edges[i][0]], vals[edges[i][1]]), i});
            //vector中绑定max值和下标,一起排序
        }
        sort(max_edge.begin(), max_edge.end(),
             [](const auto& a, const auto& b) { return a.first < b.first; });
             //sort的lambda写法
             //第三个参数直接以函数的信息写出来了
             //关注于第三个参数,直接使用auto,用于判断a是否位于b的前面
             //pair中first存储的是max信息
        vec.clear(), vec.resize(vals.size());
        for (int i = 0; i < vals.size(); i++) {//初始化vec信息
            vec[i].father = i;
            vec[i].max = vals[i];
            vec[i].size = 1;
        }
    }
    void solve() {//正式开始解题步骤
        for (int i = 0; i < max_edge.size(); i++) {
            int id = max_edge[i].second;//找到边的下标
            int id1, id2;
            id1 = edges[id][0];//找边的两个点
            id2 = edges[id][1];
            int max_id1 = vec[_find(id1)].max;
            int max_id2 = vec[_find(id2)].max;
            if (max_id1 == max_id2) {//如果最大值是一样的,维护ret
                ret += vec[_find(id1)].size * vec[_find(id2)].size;
                _union(id1, id2);
            } else {//如果不是一样的
                _union(id1, id2);
            }
        }

        ret += vals.size();//最后不要忘记了ret需要加入没一个独立的点
    }
    int numberOfGoodPaths(vector<int>& _vals, vector<vector<int>>& _edges) {
        vals = _vals;
        edges = _edges;
        init();
        solve();
        return ret;
    }
};

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

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

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

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

我们可以从 initial删除一个节点并完全移除该节点以及从该节点到任何其他节点的任何连接。

请返回移除后能够使 M(initial) 最小化的节点。如果有多个节点满足条件,返回索引 最小的节点

示例 1:

输入:graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] 输出:0

示例 2:

输入:graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1] 输出:1

示例 3:

输入:graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1] 输出:1

提示:

  • n == graph.length

  • n == graph[i].length

  • 2 <= n <= 300

  • graph[i][j]01.

  • graph[i][j] == graph[j][i]

  • graph[i][i] == 1

  • 1 <= initial.length < n

  • 0 <= initial[i] <= n - 1

  • initial 中每个整数都不同

1.

解题的思路,首先有许多个点,有毒点有正常点.

把所有的正常点相互连接的部分合并成一个集合.

遍历所有的毒点,访问所有的与毒点相连的集合,给这个集合打上标签,这个标签是这个集合与之相连的毒点.

因此这个标签实际上可能有多个,也可有可能只有一个,或者没有.

最后搞一个count数组对应所有的毒点,遍历所有的集合,如果该集合的标签只有一个,那么对应的count加上该集合的所有元素.

遍历count找到最大的count值并且找到下标,通过下标找到对应毒点的下标.

2.

需要注意的一点是,如果count值相等,需要返回较小值的毒点下标.

因此我们提前将it数组进行排序.

 
class Solution {
public:
    vector<vector<int>> g;//图的临界矩阵的存储方式
    vector<int> it;//毒点的下标,位置
    int ret;//最终结果存储遍历
    int n;//邻接矩阵的端点个数,也就是有多少个点
    struct node {//给每一个点,元素绑定一些信息,通过下标绑定所有的元素和元素对应的信息
        int father;//每一个元素的father下标,并查集的核心
        set<int> point_yuan;//每一个元素的毒点信息,实际上只需要关心集合头元素的该信息即可
        int size;//集合的元素个数
    };

    map<int, int> point_yuan_index;//将元素和下标进行映射,这样我们就可以通过元素找到对应的下标进而找到对应的所有的信息

    vector<node> v;//v表示所有元素对应的信息
    vector<int> st;//用vector模拟栈的实现,用于对并查集进行扁平化处理
    set<int> setnum;//set容器进行去重操作,存储所有的集合代表元素,代表元素的下标
    vector<int> count;//对应所有毒点的count,表示每一个毒点如果删去可以拯救的点的个数

    int _find(int i) {//并查集中查找的方法
        while (!(i == v[i].father)) {//当前节点是i位置,出口时i==v[i].father
            st.push_back(i);//对于每一个访问的下标进行存储,用栈存储走过的路
            i = v[i].father;//进入下一节点的操作
        }

        while (!st.empty()) {//对于所有走过的路,修改其father直接改为i位置,此时i是集合的代表元素的下标
            v[st.back()].father = i;
            st.pop_back();
        }

        return i;
    }

    void _union(int x, int y) {//集合的合并操作
        int rx = _find(x), ry = _find(y);//首先计算x下标,y下标所在集合的代表元素下标
        if (rx == ry)//如果所在集合一样就不需要合并
            return;
        else {//如果所在集合不一样就需要合并
            v[ry].size += v[rx].size;//对于每一个集合需要维护的信息是father和size信息
            //首先维护size信息,合并之后集合代表元素的下标是ry
            //所以需要改变ry的size信息
            v[rx].father = ry;
        }
    }

    void init() {//初始化函数,解题之前的必要操作
        sort(it.begin(), it.end());//首先给it进行排序
        n = g.size();//计算点的个数
        v.clear(), v.resize(n);//对元素信息进行分配空间
        for (int i = 0; i < n; i++) {
            v[i].father = i;//初始化father
            v[i].size = 1;//初始化size
        }
        for (int i = 0; i < it.size(); i++) {
            point_yuan_index[it[i]] = i;//毒点与下标建立映射
        }
        count.clear(), count.resize(it.size());
        ret = it[0];//一开始ret存储it第一个毒点的位置
    }
    void solve() {//正式开始解题过程
        for (int i = 0; i < n; i++) {//首先合并所有正常点
            for (int j = i + 1; j < n; j++) {
                if (g[i][j] == 1 && !point_yuan_index.count(i) &&
                    !point_yuan_index.count(j))
                    _union(i, j);//如果i,j都是正常点,并且ij之间有边,那么就合并在一起
            }
        }

        for (int i = 0; i < it.size(); i++) {//给所有的集合打标签
            int id = it[i];//找到毒点对于的下标
            for (int j = 0; j < n; j++) {
                if (id == j)
                    continue;
                if (g[id][j] == 1) {
                    v[_find(j)].point_yuan.insert(id);//set容器进行去重操作
                }
            }
        }

        
        for (int i = 0; i < n; i++) {//用set容器存储所有的集合的代表元素下标
            if (!point_yuan_index.count(i))//不考虑毒点
                setnum.insert(_find(i));
        }

        for (auto& x : setnum) {//遍历所有的集合点
            if (v[x].point_yuan.size() > 1)
                continue;
            else if (v[x].point_yuan.size() == 1) {//如果毒点连接只有一个,那么对于毒点的count值加上该集合的元素个数
                int id = point_yuan_index[*v[x].point_yuan.begin()];
                count[id] += v[x].size;
            }
        }

        int maxnum = 0;
        for (int i = 0; i < count.size(); i++) {//最后遍历count数组,找到最大值对应的毒点位置
            if (count[i] > maxnum) {
                maxnum = count[i];
                ret = it[i];
            }
        }
    }

    int minMalwareSpread(vector<vector<int>>& _graph, vector<int>& _initial) {
        g = _graph, it = _initial;
        init();
        solve();
        return ret;
    }
};

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1651629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1262403-92-8,十三肽DOTA-LGEYGFQNALIVR,是一种具有特定氨基酸序列的多肽

一、试剂信息 名称&#xff1a;DOTA-Leu-Gly-Glu-Tyr-Gly-Phe-Gln-Asn-Ala-Leu-Ile-Val-Arg-COOHCAS号&#xff1a;1262403-92-8结构式&#xff1a; 二、试剂内容 DOTA-Leu-Gly-Glu-Tyr-Gly-Phe-Gln-Asn-Ala-Leu-Ile-Val-Arg-COOH是一种具有特定氨基酸序列的多肽&#xff…

Pspice for TI学习

Pspice for TI中PSpice Part Search空白解决方法 配置环境变量 Cad_PSpice_TI_Regr_Srvr https://software-dl.ti.com/pspice/S009 重新安装2023版的Pspice Pspice安装链接 打开新安装的软件即可发现PSpice Part Search可以正常使用了 VSIN各参赛的含义 VOFF直流偏置VAMPL…

探索大语言模型在信息提取中的应用与前景

随着人工智能技术的快速发展&#xff0c;大语言模型&#xff08;LLMs&#xff09;在自然语言处理&#xff08;NLP&#xff09;领域取得了显著的进展。特别是在信息提取&#xff08;IE&#xff09;任务中&#xff0c;LLMs展现出了前所未有的潜力和优势。信息提取是从非结构化文本…

算法学习:数组 vs 链表

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f3af; 引言&#x1f6e0;️ 内存基础什么是内存❓内存的工作原理 &#x1f3af; &#x1f4e6; 数组&#xff08;Array&#xff09;&#x1f4d6; 什么是数组&#x1f300; 数组的存储&#x1f4dd; 示例代码&#…

网页翻译终极方案:DeepLX

为什么要选择 DeepL 翻译&#xff1f; DeepL 被誉为全世界最精准的机器翻译&#xff0c;比最接近他们的竞争对手准确三倍以上 看看 DeepL 和 微软翻译 的对比 &#x1f447;&#x1f447; 三句英文: Walking on eggshells during the software update. Wangs VR game is a…

c#实现音乐的“vip播放功能”

文章目录 前言1. c#窗体2. 功能3. 具体实现3.1 添加文件3.2 音乐播放3.3 其他功能 4. 整体代码和窗口5. 依赖的第三方库 前言 最近在QQ音乐里重温周杰伦的歌&#xff0c;觉得好听到耳朵怀孕&#xff0c;兴起想要下载下来反复听&#xff0c;发现QQ音乐VIP歌曲下载下来的格式居然…

【MsSQL】数据库基础 库的基本操作

目录 一&#xff0c;数据库基础 1&#xff0c;什么是数据库 2&#xff0c;主流的数据库 3&#xff0c;连接服务器 4&#xff0c;服务器&#xff0c;数据库&#xff0c;表关系 5&#xff0c;使用案例 二&#xff0c;库的操作 1&#xff0c;创建数据库 2&#xff0c;创建…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 5月8日,星期三

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年5月8日 星期三 农历四月初一 1、 我国将对法国等12国免签政策延长至2025年底&#xff0c;旅游平台加码布局入境游。 2、 财政部&#xff1a;下拨1582亿元&#xff0c;提高义务教育阶段家庭经济困难学生补助标准。 3、 4月…

STM32窗口看门狗的操作

STM32的窗口看门狗的主要功能是&#xff0c;程序过早的喂狗还有太晚喂狗&#xff0c;都会触发单片机重启&#xff0c;就是有一个时间段&#xff0c;在这个时间段内喂狗才不会触发单片机重启。 下面我就总结一下窗口看门狗的设置过程&#xff1a; 第一步&#xff1a;开启窗口看…

车辆充电桩|基于Springboot+vue的车辆充电桩管理系统的设计与实现(源码+数据库+文档)

车辆充电桩管理系统 目录 基于Springboot&#xff0b;vue的车辆充电桩管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1 前台功能模块 4.1.1 首页功能 4.1.2 用户后台管理 2 后台功能模块 4.2.1 管理员功能 4.2.2 维修员功能 四、数据库设计 五、核…

菜鸡学习netty源码(四)—— EventLoopGroup

1.概述 我们前面进行过分析,channel为netty网络操作的抽象类,EventLoop负责处理注册到其上的Channel处理的I/O事件;EventLoopGroup是一个EventLoop的分组,它可以获取到一个或者多个的EventLoop对象。 2.类关系图 NioEventLoopGroup的类继承图,蓝色部分为对应的java类,绿…

jQuery-1.语法、选择器、节点操作

jQuery jQueryJavaScriptQuery&#xff0c;是一个JavaScript函数库&#xff0c;为编写JavaScript提供了更高效便捷的接口。 jQuery安装 去官网下载jQuery&#xff0c;1.x版本练习就够用 jQuery引用 <script src"lib/jquery-1.11.2.min.js"></script>…

【经验分享】做多年大数据采集的经验分享:关于电商API数据采集接口做电商必备的电商API接口

国内主流电商平台包括&#xff1a; 1. 淘宝&#xff1a;阿里巴巴旗下的电子商务平台&#xff0c;以C2C和B2C交易为主要业务模式。 2. 天猫&#xff1a;阿里巴巴旗下的B2C电子商务平台&#xff0c;为品牌商和零售商提供销售渠道和服务。 3. 京东&#xff1a;一家以B2C为主营业务…

Linux网络部分——部署YUM仓库及NFS共享服务

目录 一、yum仓库服务 1. 软件仓库的提供方式 2.如何构建并使用ftp软件仓库&#xff08;与本地yum源方法一致&#xff09; 3.如何搭建使用yum在线源&#xff1f; 4.yum软件包下载如何保存&#xff1f; 二、NFS共享存储服务 1.存储类型 2.提供共享存储的组合 3.NFS网络…

java+jsp+Oracle+Tomcat 记账管理系统论文(完整版)

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ➡️点击免费下载全套资料:源码、数据库、部署教程、论文、答辩ppt一条龙服务 ➡️有部署问题可私信联系 ⬆️⬆️⬆️​​​​​​​⬆️…

Node.js里面 Path 模块的介绍和使用

Node.js path 模块提供了一些用于处理文件路径的小工具&#xff0c;我们可以通过以下方式引入该模块&#xff1a; var path require("path") 方法描述 序号方法 & 描述1path.normalize(p) 规范化路径&#xff0c;注意.. 和 .。2path.join([path1][, path2][,…

安全至上!群晖NAS开启SSH的正确姿势与风险防范(群晖如何开启SSH,解决群晖无法连接SSH的问题)

文章目录 💢 问题 💢🏡 演示环境 🏡💯 解决方案 💯📝 开启SSH服务📝 设置安全策略📝 远程连接SSH📝 常见问题解决⚓️ 相关链接 ⚓️💢 问题 💢 你是否遇到过需要远程管理你的群晖NAS,却发现无法通过SSH连接的窘境?SSH作为一种安全协议,可以让我们安…

C++ | Leetcode C++题解之第76题最小覆盖子串

题目&#xff1a; 题解&#xff1a; class Solution { public:unordered_map <char, int> ori, cnt;bool check() {for (const auto &p: ori) {if (cnt[p.first] < p.second) {return false;}}return true;}string minWindow(string s, string t) {for (const au…

外星人笔记本-记一次电脑发热过热缘由

背景 笔记本进行过大修&#xff0c;电池鼓包&#xff0c;还好没炸&#xff0c;因此替换电池。发现内存&#xff08;SSD&#xff09;不足&#xff0c;又增加了内存。完成后使用还算正常。但是过一段时间后&#xff0c;系统自动更新几次&#xff08;window10系统就是恶心&#x…

C语言 自定义类型——联合体

目录: 一、联合体是&#xff1f;声明计算内存大小 二、联合体的特点例如 三、联合体大小的计算规则&#xff1a; 四、应用习1习2 一、联合体是&#xff1f; 联合体和结构体差不多&#xff0c;但是其最大的区别在于联合体所有的成员共用一块内存空间。所以联合体也叫共用体。联…