2603. 收集树中金币

2023-09-21 10:56:44

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

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

收集距离当前节点距离为 2 以内的所有金币,或者
移动到树中一个相邻节点。
你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。

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

很有意思的一道题目。很难想到拓扑排序。拓扑排序经典要素,拓扑关系,度,队列。自底向上逐渐逼近。

class Solution {
    public int collectTheCoins(int[] coins, int[][] edges) {
        /**
        因为求的是最少的边数
        主要思想是先逐步删去无用的边。
        第一步是收集没有金币的叶子,然后将其与父节点的边删掉
        第二步是收集有金币的叶子,然后删掉与父节点的边及其父节点与叶节点的边
         */
        int n = coins.length;

        // 存放全局拓扑关系
        List<Integer>[] g = new ArrayList[n];
        Arrays.setAll(g, e->new ArrayList());
        // 入度
        int[] degrees = new int[n];
        var q = new ArrayDeque<Integer>();
        int leftEdges = n-1;
        for(int[] edge:edges) {
            int x = edge[0], y = edge[1];
            g[x].add(y);
            g[y].add(x);
            degrees[x] ++;
            degrees[y] ++;
        }
        // 第一步是收集没有金币的叶子,然后将其与父节点的边删掉
        for(int i=0; i<n; i++) {
            if(degrees[i] == 1 && coins[i] == 0) {
                q.offer(i);
            }
        }
        while(!q.isEmpty()) {
            leftEdges --;
            for(int node:g[q.pop()]) {
                degrees[node] --;
                if(degrees[node] == 1 && coins[node] == 0) {
                    q.offer(node);
                }
            }

        }

        // 第二步是收集有金币的叶子,然后删掉与父节点的边及其父节点与叶节点的边
        for(int i=0; i<n; i++) {
            if(degrees[i] == 1 && coins[i] == 1) {
                q.offer(i);
            }
        }
        leftEdges -= q.size(); 
        
        while(!q.isEmpty()) {
            for(int node:g[q.pop()]) {
                degrees[node] --;
                if(degrees[node] == 1) {
                    leftEdges --;
                }
            }
        }
        return Math.max(2*leftEdges, 0);
    }
}
更多推荐

【深度学习】树莓派Zero w深度学习模型Python推理

在机器学习开发过程中,当模型训练好后,接下来就要进行模型推理了,根据部署环境可分为三类场景:边缘计算:一般指手机,嵌入式设备,直接在数据生成的设备上进行推理,因为能避免将采集到的数据上传到云端,所以实时性非常好。端计算:介于云和边缘设备之间的计算平台,个人PC可以归为这一类。云计算:指云计算平台,具有强大的计算和存储能

Redis 持久化之 RDB 与 AOF 详解

Redis持久化我们知道Redis的数据是全部存储在内存中的,如果机器突然GG,那么数据就会全部丢失,因此需要有持久化机制来保证数据不会因为宕机而丢失。Redis为我们提供了两种持久化方案,一种是基于快照,另外一种是基于AOF日志。接下来就来了解一下这两种方案。操作系统与磁盘首先我们需要知道Redis数据库在持久化中扮

为何学linux及用处

目前企业使用的操作系统无非就是国产类的,windows和linux类。我们要提升自己的技能,需要学习这两款。我记得在大学时期,学习过windows以及linux,但当时觉得又不常用,就学的模棱两可。毕业之后,你会发现,其实这两种操作系统是很主流的。为什么学?下面就是一些工作中遇到的例子分享一下。我记得在企业中有次遇到数

Linux高性能服务器编程 学习笔记 第五章 Linux网络编程基础API

我们将从以下3方面讨论Linux网络API:1.socket地址API。socket最开始的含义是一个IP地址和端口对(ip,port),它唯一表示了使用TCP通信的一端,本书称其为socket地址。2.socket基础API。socket的主要API都定义在sys/socket.h头文件中,包括创建socket、命名

黄金代理前景如何,有得搞吗?

现货黄金代理这个职业时常都听人说过了,随着近期现货黄金走势不断出现行情,尤其是美国通胀严重,地缘政治局势频发,黄金走势不断获得支撑而走高。在这样的背景下,现货黄金代理这个职业的前景如何呢?其实对于现代人来说,进行现货黄金代理是很好的选择,近些年现货黄金投资人数不断上涨的趋势,所以现货黄金代理也是是除了比特币等加密货币品

go语言基础--面向对象杂谈

面向过程所谓的面向过程就是:强调的是步骤、过程、每一步都是自己亲自去实现的。面向对象所谓的面向对象其实就是找一个专门做这个事的人来做,不用关心具体怎么实现的。所以说,面向过程强调的是过程,步骤。而面向对象强调的是对象,也就是干事的人。在程序中,可以通过属性和方法(函数)来描述类。属性就是特征,方法(函数)就是行为。面向

ARM架构过程调用标准AAPCS(学习)

AAPCS是ARM架构的处理器规定的一些标准。参数和返回值传递,对于简单的情况,输入参数由R0-R3分别用来记录第1到4个参数。当传递的参数超过4个时,就需要借助栈来保存参数。函数的返回值通常保存在R0中,若返回值为64位,R1也用来保存返回值。函数调用中的寄存器用法。函数或子程序应该保持R4-R11、R13和R14的

Vue学习笔记总结

目录1、Vue核心1.1什么是vue?1.2什么是mvvm?​编辑1.3插值表达式{{}}2.Vue中的常用指令2.1内容渲染指令2.2条件渲染指令2.3事件绑定指令2.4属性绑定指令2.5列表渲染指令2.5.1小案例-小黑的书架2.6v-for中的key2.7双向绑定指令3.指令修饰符3.1什么是指令修饰符?3.2按

esbuild中文文档-基础配置项(General options - Tsconfig、Tsconfig raw)

文章目录TsconfigTsconfigraw结语哈喽,大家好!我是「励志前端小黑哥」,我带着最新发布的文章又来了!老规矩,小手动起来~点赞关注不迷路!esbuild简单介绍esbuild为了突破了JavaScript语言的瓶颈,采用了Go语言编写,构建速度与同代码量下的webpack对比提升在10倍以上,开创了构建工

(并查集) 1971. 寻找图中是否存在路径 ——【Leetcode每日一题】

❓1971.寻找图中是否存在路径难度:简单有一个具有n个顶点的双向图,其中每个顶点标记从0到n-1(包含0和n-1)。图中的边用一个二维整数数组edges表示,其中edges[i]=[ui,vi]表示顶点ui和顶点vi之间的双向边。每个顶点对由最多一条边连接,并且没有顶点存在与自身相连的边。请你确定是否存在从顶点sou

为何学linux及用处

目前企业使用的操作系统无非就是国产类的,windows和linux类。我们要提升自己的技能,需要学习这两款。我记得在大学时期,学习过windows以及linux,但当时觉得又不常用,就学的模棱两可。毕业之后,你会发现,其实这两种操作系统是很主流的。为什么学?下面就是一些工作中遇到的例子分享一下。我记得在企业中有次遇到数

热文推荐