LeetCode:2603. 收集树中金币 详解(2023.9.21 C++)

2023-09-22 01:26:53

目录

2603. 收集树中金币

题目描述:

实现代码与解析:

拓扑 + bfs

原理思路:


2603. 收集树中金币

题目描述:

        给你一个 n 个节点的无向无根树,节点编号从 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。再给你一个长度为 n 的数组 coins ,其中 coins[i] 可能为 0 也可能为 1 ,1 表示节点 i 处有一个金币。

一开始,你需要选择树中任意一个节点出发。你可以执行下述操作任意次:

  • 收集距离当前节点距离为 2 以内的所有金币,或者
  • 移动到树中一个相邻节点。

你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。

如果你多次经过一条边,每一次经过都会给答案加一。

示例 1:

输入:coins = [1,0,0,0,0,1], edges = [[0,1],[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:从节点 2 出发,收集节点 0 处的金币,移动到节点 3 ,收集节点 5 处的金币,然后移动回节点 2 。

示例 2:

输入:coins = [0,0,0,1,1,0,0,1], edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[5,6],[5,7]]
输出:2
解释:从节点 0 出发,收集节点 4 和 3 处的金币,移动到节点 2 处,收集节点 7 处的金币,移动回节点 0 。

提示:

  • n == coins.length
  • 1 <= n <= 3 * 104
  • 0 <= coins[i] <= 1
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • edges 表示一棵合法的树。

实现代码与解析:

拓扑 + bfs

class Solution {
public:
    vector<int> h = vector<int>(30010, -1), e = vector<int>(60010, 0), ne = vector<int>(60100, 0), d = vector<int>(30010, 0);
    int idx = 0;
    void add(int a, int b)
    {
        e[idx] = b; ne[idx] = h[a]; h[a] = idx++;
    } 

    int collectTheCoins(vector<int>& coins, vector<vector<int>>& edges) {
        

        int n = coins.size(); // 结点个数
        int rest = n; // 剩余的结点 rest翻译剩余

        for (auto t: edges)
        {
            int a = t[0];
            int b = t[1];
            add(a, b), add(b, a); // 加无向边
            d[a]++, d[b]++; // 两个结点 入度++
        }

        queue<int> q;
        // 删除树中 无金币 的叶子结点
        for (int i = 0; i < n; i++) 
            if (d[i] == 1 && !coins[i]) q.push(i); // 无向边,入度为1的是叶子节点,要和有向的区分开
        while (q.size())
        {
            int t = q.front();
            d[t]--; // d[t]-- 此结点从1变为0 相当于删除此节点和边
            rest--; // 剩余结点--
            q.pop();
            for (int i = h[t]; ~i; i = ne[i])
            {
                int j = e[i];
                d[j]--; // 相邻结点 入度--
                if (d[j] == 1 && !coins[j]) q.push(j); // 入度为1的继续入队,因为有可能有新0金币结点 因为删除 变为叶子结点
            }
        }

        // 拓扑(拓扑排序中的两个去除环节 执行两次) 去除叶子结点 两次
        for (int i = 0; i < 2; i++)
        {
            queue<int> qq;

            for (int i = 0; i < n; i++)
                if (d[i] == 1) qq.push(i); // 叶子结点入队
            
            while(qq.size())
            {
                int t = qq.front();
                d[t]--;
                qq.pop();
                rest--;
                for (int i = h[t]; ~i; i = ne[i])
                {
                    int j = e[i];
                    d[j]--;
                } 
            }
        }

        return rest == 0 ? 0 : 2 * (rest - 1);
    }
};

原理思路:

        首先去掉无金币的叶子结点,因为无金币的叶子结点是不需要收集的,收集绝对会浪费步数,以它为起点也会浪费步数,所以可以直接删除掉(连同它相连的边),直到叶子结点中没有无金币的。

        然后从叶子节点一层一层往剥,举个例子,比如一个三层的饼干,如果左右扩散为1,肯定是要选最中间的那层,就可以获得整个饼干(也就是去掉外面两侧,剩的那一层),如果是4层饼干,那么我们就要选中间两层(也就是去掉外面两侧,剩下的那二层)

        扩散为2,或者更多层数,也是同理,就是逆像思维从外一层一层往里剥(也就是拓扑),最后剩下的就一定是我们需要遍历。

        最后剩 n 个结点的话,需要全部走完,n个节点 n - 1 条边,每条边走两次才能回到原位,所以答案就为 2 * (n - 1)

        核心思想:删除没有金币的叶子节点,然后连续两次删除所有叶子节点。最终结果基于树中剩余节点的数量计算得出。

        代码的话,其实思想明白了就很好写了,就是基础的bfs和拓扑代码,如果不会拓扑可以看看我之前写的拓扑排序的写法和有关的题

拓扑排序详解(带有C++模板)_Cosmoshhhyyy的博客-CSDN博客

LeetCode:207. 课程表、210. 课程表 II(拓扑排序 C++)_Cosmoshhhyyy的博客-CSDN博客

        注意点:这里是无向图,我这种图的邻接表写法,需要二倍的边的个数,而且平时拓扑都是为0的时候入队,这里是1,因为是无向图嘛。最后如果没有节点,就返回0

更多推荐

华为OD机考算法题:垃圾信息拦截

目录题目部分解读与分析代码实现题目部分题目垃圾信息拦截难度难题目说明大众对垃圾短信深恶痛绝,希望能对垃圾短信发送者进行识别,为此,很多软件增加了垃圾短信识别机制。经分析,发现正常用户的短信通常具备交互性,而垃圾短信往往都是大量单向的短信,按照如下规则进行垃圾短信识别:本题中,发送者A符合以下条件之一的,则认为A是垃圾短

SVN的基本使用

一、SVN介绍SVN(Subversion)是一个开源的版本控制系统,它专门用于管理文件和目录的变更。SVN提供了一种集中式的版本控制方案,其中有一个中央仓库存储所有文件的历史记录和变更。SVN使用方式相对简单,可以通过命令工具或可视化客户端进行操作,下面主要是SVN客户端的操作方式二、安装客户端软件进入官网下载tor

基于 MATLAB 的电力系统动态分析研究【IEEE9、IEEE68系节点】

💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。⛳️座右铭:行百里者,半于九十。📋📋📋本文目录如下:🎁🎁🎁目录💥1概述📚2运行结果2.1IEEE9节点2.2IEEE68节点🎉3参考文献🌈4Matlab代码、数据、文章💥1概述

什么是云计算中的资源调度,解释资源调度的挑战和算法

1、什么是云计算中的资源调度,解释资源调度的挑战和算法。在云计算中,资源调度(ResourceScheduling)指的是如何在不同类型的资源(例如计算资源、存储资源、网络资源等)之间合理地分配和调度资源,以实现高效的资源管理和任务执行。资源调度的目标是提高系统的可用性、可靠性和性能。然而,资源调度面临着一些挑战。首先

敏捷开发工具:提升软件研发效率的重要利器

在当今的软件开发领域,敏捷开发方法越来越受到推崇。敏捷开发的核心是灵活应对需求变化,以快速迭代的方式不断优化产品。为了助力敏捷开发的实施,各种敏捷开发工具应运而生。本文将介绍几种常用的敏捷开发工具,阐述其特点、应用场景及优缺点,最后对敏捷开发工具的重要性进行总结。一、敏捷开发工具介绍Leangoo领歌:Leangoo领

什么是Selenium?使用Selenium进行自动化测试!

你知道什么是Selenium吗?你知道为什么要使用它吗?答案就在本文中,很高兴能够与你共飧。自动化测试正席卷全球,Selenium认证是业界最抢手的技能之一。什么是Selenium?Selenium是一种开源工具,用于在Web浏览器上执行自动化测试(使用任何Web浏览器进行Web应用程序测试)。等等,先别激动,让我再次

【详细图文】Windows下安装RustRover和配置Rust环境

前言Rust已经火了挺长时间了,连微软的Windows内核都用它来重新改写,可想而知其厉害之处。之前有看过Rust的教程,但一直没有去尝试。今天看到JetBrains出了Rust专用的IDE:RustRover。作为JetBrains的粉丝,决定进行一次部署实践。本文是从工具安装和环境部署到HelloWorld,作为一

kubernetes popeye 巡检

文章目录1.简介2.安装3.本地4.容器1.简介Popeye是一个实用程序,可以扫描实时Kubernetes集群,并报告部署的资源和配置的潜在问题。它根据部署的内容而不是磁盘上的内容来清理集群。通过扫描您的集群,它可以检测错误配置,并帮助您确保最佳实践到位,从而防止未来的麻烦。它旨在减少在野外操作Kubernetes集

搭建Android自动化python+appium环境

一.需要软件JDK:JAVA安装后配置JDK环境SDK:SDK下载后配置adb环境Python:pyhton语言Pycharm:python脚本编译工具Appium-python-client:pyhton中的库Appium客户端二.搭建步骤1.配置JDK环境①.下载安装java:https://www.oracle.

解码癌症预测的密码:可解释性机器学习算法SHAP揭示XGBoost模型的预测机制

一、引言癌症是全球范围内健康领域的一大挑战,早期预测和诊断对于提高治疗效果和生存率至关重要。机器学习在癌症预测中发挥了重要作用,可以从临床数据中学习并构建癌症预测模型,帮助医生进行早期检测和干预,提高患者的生活质量和预后结果。然而,机器学习模型的黑盒性质限制了其在临床实践中的应用。可解释的机器学习被广泛关注,它不仅能够

selenium+python实现基本自动化测试

安装selenium打开命令控制符输入:pipinstall-Uselenium火狐浏览器安装firebug:www.firebug.com,调试所有网站语言,调试功能SeleniumIDE是嵌入到Firefox浏览器中的一个插件,实现简单的浏览器操作的录制与回放功能,IDE录制的脚本可以可以转换成多种语言,从而帮助我

热文推荐