2477. 到达首都的最少油耗 : 逐步讲解最低油耗求解思路

题目描述

这是 LeetCode 上的 「2477. 到达首都的最少油耗」 ,难度为 「中等」

Tag : 「DFS」

给你一棵 n 个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从 0n - 1,且恰好有 n - 1 条路。

0 是首都。给你一个二维整数数组 roads,其中 ,表示城市 之间有一条 双向路 。

每个城市里有一个代表,他们都要去首都参加一个会议。

每座城市里有一辆车。给你一个整数 seats 表示每辆车里面座位的数目。

城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车。相邻城市之间一辆车的油耗是一升汽油。

请你返回到达首都最少需要多少升汽油。

示例 1: alt

输入:roads = [[0,1],[0,2],[0,3]], seats = 5

输出:3

解释:
- 代表 1 直接到达首都,消耗 1 升汽油。
- 代表 2 直接到达首都,消耗 1 升汽油。
- 代表 3 直接到达首都,消耗 1 升汽油。
最少消耗 3 升汽油。

示例 2: alt

输入:roads = [[3,1],[3,2],[1,0],[0,4],[0,5],[4,6]], seats = 2

输出:7

解释:
- 代表 2 到达城市 3 ,消耗 1 升汽油。
- 代表 2 和代表 3 一起到达城市 1 ,消耗 1 升汽油。
- 代表 2 和代表 3 一起到达首都,消耗 1 升汽油。
- 代表 1 直接到达首都,消耗 1 升汽油。
- 代表 5 直接到达首都,消耗 1 升汽油。
- 代表 6 到达城市 4 ,消耗 1 升汽油。
- 代表 4 和代表 6 一起到达首都,消耗 1 升汽油。
最少消耗 7 升汽油。

示例 3: alt

输入:roads = [], seats = 1

输出:0

解释:没有代表需要从别的城市到达首都。

提示:

  • roads 表示一棵合法的树。

DFS

将双向图看作是以节点 0 为根的有向树,从每个节点出发往 0 前行,可看作是自底向上的移动过程。

alt

seats = 1 时,每个节点前往 0 的过程相互独立,总油耗为每节点到 0 的最短距离之和。

seats 不为 1 时,考虑组成顺风车,此时总的油耗不该超过 seats = 1 的情况。

不难发现,「只有「深度大的节点,在前往 0 过程中,搭乘深度小顺风车」可减少油耗」(例如在上图节点 3 在经过节点 1 时搭乘顺风车,可与节点 1 合计使用一份油耗前往到 0),否则如果是深度小的节点先往深度大的节点走,再一同前往 0,会额外多经过某些边,产生不必要的油耗。

考虑组成顺风车时,总油耗该如何计算。

基于上述分析,无论 seats 是否为 (是否组成顺风车),每个节点前往 0 的路径总是不变,即经过的边固定不变,必然是自底向上。

因此我们可统计每条边会被多少个节点经过,通过 DFS 统计「以每个节点为根时,子树的节点数量」即是经过该节点往上的边。

alt

Java 代码:

class Solution {
    int N = 100010, M = 2 * N, idx = 0;
    int[] he = new int[N], e = new int[M], ne = new int[M];
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx++;
    }
    long ans = 0;
    public long minimumFuelCost(int[][] roads, int seats) {
        int n = roads.length + 1;
        Arrays.fill(he, -1);
        for (int[] r : roads) {
            int a = r[0], b = r[1];
            add(a, b); add(b, a);
        }
        dfs(0, -1, seats);
        return ans;
    }
    int dfs(int u, int fa, int t) {
        int cnt = 1;
        for (int i = he[u]; i != -1; i = ne[i]) {
            int j = e[i];
            if (j == fa) continue;
            cnt += dfs(j, u, t);
        }
        if (u != 0) ans += Math.ceil(cnt * 1.0 / t);
        return cnt;
    }
}

C++ 代码:

class Solution {
public:
    int N = 100010, M = 2 * N, idx = 0;
    int he[100010], e[200020], ne[200020];
    long long ans = 0;
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx++;
    }
    long long minimumFuelCost(vector<vector<int>>& roads, int seats) {
        int n = roads.size() + 1;
        memset(he, -1sizeof(he));
        for (auto& r : roads) {
            int a = r[0], b = r[1];
            add(a, b); add(b, a);
        }
        dfs(0-1, seats);
        return ans;
    }
    int dfs(int u, int fa, int t) {
        int cnt = 1;
        for (int i = he[u]; i != -1; i = ne[i]) {
            int j = e[i];
            if (j == fa) continue;
            cnt += dfs(j, u, t);
        }
        if (u != 0) ans += ceil(cnt * 1.0 / t);
        return cnt;
    }
};

Python 代码:

class Solution:
    def minimumFuelCost(self, roads: List[List[int]], seats: int) -> int:
        n, m, ans = len(roads) + 1, len(roads) * 2 + 10
        he, e, ne, idx = [-1] * n, [0] * m, [0] * m, 0

        def add(a, b):
            nonlocal idx
            e[idx] = b
            ne[idx] = he[a]
            he[a] = idx
            idx += 1

        def dfs(u, fa, t):
            nonlocal ans
            cnt = 1
            i = he[u]
            while i != -1:
                j, i = e[i], ne[i]
                if j == fa: continue
                cnt += dfs(j, u, t)
            if u != 0:
                ans += math.ceil(cnt * 1.0 / t)
            return cnt

        for a, b in roads:
            add(a, b)
            add(b, a)
        dfs(0-1, seats)
        return ans

TypeScript 代码:

function minimumFuelCost(roads: number[][], seats: number): number {
    const n = roads.length + 1, m = n * 2;
    const he = Array(n).fill(-1), e = Array(m).fill(0), ne = Array(m).fill(0);
    let ans = 0, idx = 0;
    const add = function(a: number, b: number{
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx++;
    }
    const dfs = function(u: number, fa: number, t: number): number {
        let cnt = 1;
        for (let i = he[u]; i != -1; i = ne[i]) {
            const j = e[i];
            if (j == fa) continue;
            cnt += dfs(j, u, t);
        }
        if (u != 0) ans += Math.ceil(cnt * 1.0 / t);
        return cnt;
    }
    for (const [a, b] of roads) {
        add(a, b); add(b, a);
    }
    dfs(0-1, seats);
    return ans;
};
  • 时间复杂度:建图复杂度为 ;递归统计每个子树节点数量并计算油耗复杂度 ;整体复杂度为
  • 空间复杂度:

最后

这是我们「刷穿 LeetCode」系列文章的第 No.2477 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全网最新最牛的Appium自动化:Appium常用操作之TouchAction操作

TouchAction操作 Appium的辅助类&#xff0c;主要针对手势操作&#xff0c;比如滑动、长按、拖动等。其原理是将一系列的动作放在一个链条中&#xff0c;然后将该链条传递给服务器。服务器接受到该链条后&#xff0c;解析各个动作&#xff0c;逐个执行。 TouchAction类支持的动…

解决:docx.opc.exceptions.PackageNotFoundError: Package not found at ‘xxx’

解决&#xff1a;docx.opc.exceptions.PackageNotFoundError: Package not found at ‘xxx’ 文章目录 解决&#xff1a;docx.opc.exceptions.PackageNotFoundError: Package not found at ‘xxx’背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景…

深度学习TensorFlow2基础知识学习前半部分

目录 测试TensorFlow是否支持GPU&#xff1a; 自动求导&#xff1a; 数据预处理 之 统一数组维度 定义变量和常量 训练模型的时候设备变量的设置 生成随机数据 交叉熵损失CE和均方误差函数MSE 全连接Dense层 维度变换reshape 增加或减小维度 数组合并 广播机制&#…

MYSQL8用户权限配置详解

单位的系统性能问题需要把Mysql5升级到Mysql8&#xff0c;需要用到Mysql8的一些特性来提升系统的性能。 配置用户权限过程中发现一些问题&#xff0c;学习并记录一下。 目录 一、环境 二、MySQL8 用户权限 2.1 账号管理权限 2.1.1 连接数据库 2.1.2 账号权限配置 2.2 密码…

从开发到测试,你需要掌握哪些必备测试技能?

一、为什么从开发转测试 我从2019年5月开始从一名java开发女程序猿正式转为测试开发工程师&#xff0c;原因除了机缘凑巧之外&#xff0c;当然是因为这个行业对测试工程师的要求已经越来越高&#xff0c;简单做些UI脚本录制和回放的自动化&#xff0c;参考度娘写出框架demo却不…

二叉树求叶子节点

以这个图展示叶子节点的求取 项目结构 项目代码截图&#xff1a;使用递归的方式求取二叉树的叶子节点&#xff08;递归指的是函数自己调用自己的过程&#xff09; 具体代码展示 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #includ…

全网最新最全的Appium自动化:Appium常用操作之等待操作

等待机制&#xff1a; 为了保证脚本的稳定性&#xff0c;有时候需要引入等待时间&#xff0c;等待页面加载元素后再进行操作&#xff0c;主要有三种等待时间设置方式。 方式一&#xff1a; sleep()&#xff1a;固定等待时间设置&#xff0c;python的time包里提供了休眠方法sle…

Clion自定义管理和配置软件构建过程的工具(代替CMake)构建程序

在公司由于需要x86环境和其他arm环境&#xff0c;同时需要使用公司自定义的mine_x86或者mine_orin对代码进行编译。 编译命令如下mine_x86 build -Dlocal1 -j8,为使用Clion对程序进行调试&#xff0c;需要对程序进行设置。方便调试代码时能够断点查看变量。尝试了很多次&#…

什么是网络爬虫?有什么用?怎么爬?

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 【导读】 网络爬虫也叫做网络机器人&#xff0c;可以代替人们自动地在互联网中进行数据信息的采集与整理。 在大数据时代&#xff0c;信息的采集是一项重要的工作&#xff0c;如果单纯靠人力进行信息采集&#xff0c;不仅低…

作业12.5

1.定义一个基类 Animal&#xff0c;其中有一个虛函数perform&#xff08;)&#xff0c;用于在子类中实现不同的表演行为。 #include <iostream>using namespace std; class Animal { private:int weight; public:Animal(){}Animal(int weight):weight(weight){}virtual …

temu最近数据:拼多多旗下跨境电商平台的业绩持续增长

据最近的报道和数据显示&#xff0c;拼多多旗下的跨境电商平台Temu在2023年第三季度取得了显著的业绩增长。销售额突破50亿美元&#xff0c;市场份额不断扩大&#xff0c;用户数量迅速增长。本文将深入探讨Temu的业绩增长、市场份额、用户增长以及其营销策略。 先给大家推荐一款…

批量给文件名加相同后缀的两个方法

如何批量给文件名加相同后缀&#xff1f;文件处理是每个上班族需要面对的工作&#xff0c;并且文件处理能力的高低也体现了我们工作能力的高低&#xff0c;文件处理中就包含文件名称的修改&#xff0c;修改文件名是非常简单的&#xff0c;通过点击软件重命名就可以进行操作&…

应用案例 | 基于三维视觉的汽车零件自动化拧紧解决方案

​Part.1 引言 随着人们生活水平的提高&#xff0c;汽车作为理想的代步工具&#xff0c;逐渐成为人们生活中不可或缺的一部分。汽车的广泛应用&#xff0c;大大增加了汽车制造业的负荷。因此&#xff0c;如何提高生产效率和汽车性能&#xff0c;成为汽车制造业的首要关注话题。…

AI之火是如何燎原的?始于马斯克与佩奇的一场激辩

丨划重点 ①在2015年, 马斯克44岁生日派对上&#xff0c;他与谷歌联合创始人佩奇曾就AI产生严重分歧&#xff0c;甚至终结了十多年的友谊。佩奇认为人类最终将与AI机器融合&#xff0c;将会有许多种智能争夺资源, 马斯克则担心机器可能会毁灭人类。 ②在收购AI创企DeepMind时…

智慧电力在线监测系统

智慧电力在线监测系统是一种基于物联网、云计算、大数据和人工智能等技术的智能化监控系统&#xff0c;用于实时监测电力设备的运行状态和电能质量&#xff0c;及时发现和处理电力系统中存在的问题&#xff0c;提高电力系统的安全性和可靠性。依托电易云-智慧电力物联网&#x…

Linux系统编程:并发与信号总结

并发 并发是指两个或多个同时独立进行的活动。在计算机系统中&#xff0c;并发指的是同一个系统中多个独立活动同时进行&#xff0c;而非依次进行。 并发在计算机系统中的表现&#xff1a; 一个时间段中有几个程序都处于已启动运行到运行完毕之间&#xff0c;且这几个程序都是…

spring boot 2 升级到 spring boot 3 后文件上传失败

背景 项目需要&#xff0c;要求升级 spring boot 2.7 到 spring boot 3.2&#xff0c;升级过程中发现很多不兼容问题&#xff0c;下面说明文件上传失败的解决方案。 问题 spring boot 2 中不需要额外的配置&#xff0c;直接在 Controller 中配置 MultipartFile 接收页面传的…

创建 Python Docker 镜像的完整指南

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python和Docker是两个极其流行的技术&#xff0c;结合它们可以创建强大的应用程序。Docker允许将应用程序及其依赖项打包到一个独立的容器中&#xff0c;而Python则提供了丰富的库和工具来开发应用程序。本文将提…

C++ 12.5作业

以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一位讲解员&…

国产接口测试工具APIpost

说实话&#xff0c;了解APIpost是因为&#xff0c;我的所有接口相关的文章下&#xff0c;都有该APIpost水军的评论&#xff0c;无非就是APIpost是中文版的postman&#xff0c;有多么多么好用&#xff0c;虽然咱也还不是什么啥网红&#xff0c;但是不知会一声就乱在评论区打广告…