【教3妹学编程-算法题】最小化旅行的价格总和

2023-12-06 17:52

本文主要是介绍【教3妹学编程-算法题】最小化旅行的价格总和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一夜暴富

3妹:2哥2哥,你有没有看到新闻, 有人中了2.2亿彩票大奖!
2哥 : 看到了,2.2亿啊, 一生一世也花不完。
3妹:为啥我就中不了呢,不开心呀不开心。
2哥 : 得了吧,你又不买彩票,还是脚踏实地的好~
3妹:小富靠勤,中富靠德,大富靠命, 可能是我命不好。
2哥 : 话说如果你有了钱,想要干嘛呀?
3妹:旅行,到处去旅行
2哥:就知道会有这一项, 我今天看到一个关于旅行的题目,让我也来考考你吧~

考考你

题目:

现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条边。

每个节点都关联一个价格。给你一个整数数组 price ,其中 price[i] 是第 i 个节点的价格。

给定路径的 价格总和 是该路径上所有节点的价格之和。

另给你一个二维整数数组 trips ,其中 trips[i] = [starti, endi] 表示您从节点 starti 开始第 i 次旅行,并通过任何你喜欢的路径前往节点 endi 。

在执行第一次旅行之前,你可以选择一些 非相邻节点 并将价格减半。

返回执行所有旅行的最小价格总和。

示例 1:
image.png

输入:n = 4, edges = [[0,1],[1,2],[1,3]], price = [2,2,10,6], trips = [[0,3],[2,1],[2,3]]
输出:23
解释:
上图表示将节点 2 视为根之后的树结构。第一个图表示初始树,第二个图表示选择节点 0 、2 和 3 并使其价格减半后的树。
第 1 次旅行,选择路径 [0,1,3] 。路径的价格总和为 1 + 2 + 3 = 6 。
第 2 次旅行,选择路径 [2,1] 。路径的价格总和为 2 + 5 = 7 。
第 3 次旅行,选择路径 [2,1,3] 。路径的价格总和为 5 + 2 + 3 = 10 。
所有旅行的价格总和为 6 + 7 + 10 = 23 。可以证明,23 是可以实现的最小答案。

示例 2:
image.png

输入:n = 2, edges = [[0,1]], price = [2,2], trips = [[0,0]]
输出:1
解释:
上图表示将节点 0 视为根之后的树结构。第一个图表示初始树,第二个图表示选择节点 0 并使其价格减半后的树。
第 1 次旅行,选择路径 [0] 。路径的价格总和为 1 。
所有旅行的价格总和为 1 。可以证明,1 是可以实现的最小答案。

提示:

1 <= n <= 50
edges.length == n - 1
0 <= ai, bi <= n - 1
edges 表示一棵有效的树
price.length == n
price[i] 是一个偶数
1 <= price[i] <= 1000
1 <= trips.length <= 100
0 <= starti, endi <= n - 1

思路:

思考

深度优先搜索 + 动态规划,
为了使旅行的价格总和最小,那么每次旅行的路径必定是最短路径。根据题意,每次旅行 trips[i] 都是独立的,因此我们可以依次开始旅行 trips[i],并且用数组 count记录节点在旅行中被经过的次数。记旅行 trips[i]的起点和终点分别为 starti和 endi,那么我们以 starti为树的根节点,对树进行深度优先搜索,对于如果节点 node\textit{node}node 的子树(包含它本身)包含节点 endi ,那么我们将 count[node] 加一。

java代码:

class Solution {private List<Integer>[] g, qs;private int[] diff, father, color, price;public int minimumTotalPrice(int n, int[][] edges, int[] price, int[][] trips) {g = new ArrayList[n];Arrays.setAll(g, e -> new ArrayList<>());for (var e : edges) {int x = e[0], y = e[1];g[x].add(y);g[y].add(x); // 建树}qs = new ArrayList[n];Arrays.setAll(qs, e -> new ArrayList<>());for (var t : trips) {int x = t[0], y = t[1];qs[x].add(y); // 路径端点分组if (x != y) qs[y].add(x);}pa = new int[n];for (int i = 1; i < n; ++i)pa[i] = i;diff = new int[n];father = new int[n];color = new int[n];tarjan(0, -1);this.price = price;var p = dfs(0, -1);return Math.min(p[0], p[1]);}// 并查集模板private int[] pa;private int find(int x) {if (pa[x] != x)pa[x] = find(pa[x]);return pa[x];}private void tarjan(int x, int fa) {father[x] = fa;color[x] = 1; // 递归中for (int y : g[x])if (color[y] == 0) { // 未递归tarjan(y, x);pa[y] = x; // 相当于把 y 的子树节点全部 merge 到 x}for (int y : qs[x])// color[y] == 2 意味着 y 所在子树已经遍历完// 也就意味着 y 已经 merge 到它和 x 的 lca 上了if (y == x || color[y] == 2) { // 从 y 向上到达 lca 然后拐弯向下到达 x++diff[x];++diff[y];int lca = find(y);--diff[lca];int f = father[lca];if (f >= 0) {--diff[f];}}color[x] = 2; // 递归结束}private int[] dfs(int x, int fa) {int notHalve = 0, halve = 0, cnt = diff[x];for (int y : g[x])if (y != fa) {var p = dfs(y, x); // 计算 y 不变/减半的最小价值总和notHalve += Math.min(p[0], p[1]); // x 不变,那么 y 可以不变,可以减半,取这两种情况的最小值halve += p[0]; // x 减半,那么 y 只能不变cnt += p[2]; // 自底向上累加差分值}notHalve += price[x] * cnt; // x 不变halve += price[x] * cnt / 2; // x 减半return new int[]{notHalve, halve, cnt};}
}

这篇关于【教3妹学编程-算法题】最小化旅行的价格总和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/462745

相关文章

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同