2023-10-04:用go语言,现有一棵无向、无根的树,树中有 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 。

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

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

输入:n = 4, edges = [[0,1],[1,2],[1,3]], price = [2,2,10,6], trips = [[0,3],[2,1],[2,3]]。

输出:23。

来自左程云

答案2023-10-04:

大体过程如下:

1.构建图:根据输入的edges构建无向图,使用邻接表存储每个节点的邻居节点。

2.初始化查询数组:根据trips初始化查询数组,将每个旅行的起点和终点加入到对应节点的查询数组中。

3.初始化并查集:初始化一个并查集,用于保存节点的父节点信息和标签。将每个节点的父节点初始化为自身,标签初始化为-1。

4.进行Tarjan算法:从根节点开始遍历树,使用递归的方式进行深度优先搜索。

  • 对于每个节点cur,记录其父节点father。

  • 遍历cur的邻居节点next,如果next不等于father,进行递归操作。

  • 递归操作结束后,将cur和next节点合并,设置它们的标签为cur。

  • 对于cur节点的查询数组中的每个查询,如果查询的终点的标签不为-1,说明该查询经过cur节点,记录查询的终点标签为最低公共祖先节点。

5.计算每个节点的旅行个数:遍历旅行数组,统计每个节点作为起点或终点的旅行个数。

  • 对于每个旅行,起点和终点的旅行个数加1,最低公共祖先节点的旅行个数减1。

  • 如果最低公共祖先节点的父节点不为-1,最低公共祖先节点的父节点的旅行个数减1。

6.使用深度优先搜索计算价格总和:从根节点开始,使用递归的方式进行深度优先搜索。

  • 对于每个节点cur,计算不选择减半价格的情况下的总价格no和选择减半价格的情况下的总价格

  • 遍历cur的邻居节点next,如果next不等于father,进行递归操作。

  • 更新no和yes的值。

7.返回最小价格总和:取no和yes中较小的值作为最小价格总和。

总的时间复杂度:O(n)(遍历节点和邻居节点) + O(m)(遍历查询数组) + O(n)(遍历旅行数组) + O(n)(遍历节点和邻居节点) = O(n + m)

总的额外空间复杂度:O(n)(存储图) + O(m)(存储查询数组) + O(n)(存储父节点信息) + O(n)(存储旅行个数) + O(n)(存储价格总和) = O(n + m)

go完整代码如下:

package main

import (
"fmt"
"math"
) func minimumTotalPrice(n int, edges [][]int, price []int, trips [][]int) int {
graph := make([][]int, n)
queries := make([][][]int, n)
for i := 0; i < n; i++ {
graph[i] = make([]int, 0)
queries[i] = make([][]int, 0)
}
for _, edge := range edges {
graph[edge[0]] = append(graph[edge[0]], edge[1])
graph[edge[1]] = append(graph[edge[1]], edge[0])
}
m := len(trips)
lcs := make([]int, m)
for i := 0; i < m; i++ {
if trips[i][0] == trips[i][1] {
lcs[i] = trips[i][0]
} else {
queries[trips[i][0]] = append(queries[trips[i][0]], []int{trips[i][1], i})
queries[trips[i][1]] = append(queries[trips[i][1]], []int{trips[i][0], i})
}
}
uf := &UnionFind{}
uf.init(n)
fathers := make([]int, n)
tarjan(graph, 0, -1, uf, queries, fathers, lcs)
cnts := make([]int, n)
for i := 0; i < m; i++ {
cnts[trips[i][0]]++
cnts[trips[i][1]]++
cnts[lcs[i]]--
if fathers[lcs[i]] != -1 {
cnts[fathers[lcs[i]]]--
}
}
dfs(graph, 0, -1, cnts)
ans := dp(graph, 0, -1, cnts, price)
return int(math.Min(float64(ans[0]), float64(ans[1])))
} func tarjan(graph [][]int, cur int, father int, uf *UnionFind, queries [][][]int, fathers []int, lcs []int) {
fathers[cur] = father
for _, next := range graph[cur] {
if next != father {
tarjan(graph, next, cur, uf, queries, fathers, lcs)
uf.union(cur, next)
uf.setTag(cur, cur)
}
}
for _, query := range queries[cur] {
tag := uf.getTag(query[0])
if tag != -1 {
lcs[query[1]] = tag
}
}
} func dfs(graph [][]int, cur int, father int, cnts []int) {
for _, next := range graph[cur] {
if next != father {
dfs(graph, next, cur, cnts)
cnts[cur] += cnts[next]
}
}
} func dp(graph [][]int, cur int, father int, cnts []int, price []int) []int {
no := price[cur] * cnts[cur]
yes := (price[cur] / 2) * cnts[cur]
for _, next := range graph[cur] {
if next != father {
nextAns := dp(graph, next, cur, cnts, price)
no += int(math.Min(float64(nextAns[0]), float64(nextAns[1])))
yes += nextAns[0]
}
}
return []int{no, yes}
} type UnionFind struct {
father []int
size []int
tag []int
help []int
} func (uf *UnionFind) init(n int) {
uf.father = make([]int, n)
uf.size = make([]int, n)
uf.tag = make([]int, n)
uf.help = make([]int, n)
for i := 0; i < n; i++ {
uf.father[i] = i
uf.size[i] = 1
uf.tag[i] = -1
}
} func (uf *UnionFind) find(i int) int {
size := 0
for i != uf.father[i] {
uf.help[size] = i
i = uf.father[i]
size++
}
for size > 0 {
size--
uf.father[uf.help[size]] = i
}
return i
} func (uf *UnionFind) union(i, j int) {
fi := uf.find(i)
fj := uf.find(j)
if fi != fj {
if uf.size[fi] >= uf.size[fj] {
uf.father[fj] = fi
uf.size[fi] += uf.size[fj]
} else {
uf.father[fi] = fj
uf.size[fj] += uf.size[fi]
}
}
} func (uf *UnionFind) setTag(i, t int) {
uf.tag[uf.find(i)] = t
} func (uf *UnionFind) getTag(i int) int {
return uf.tag[uf.find(i)]
} func main() {
n := 4
edges := [][]int{{0, 1}, {1, 2}, {1, 3}}
price := []int{2, 2, 10, 6}
trips := [][]int{{0, 3}, {2, 1}, {2, 3}}
result := minimumTotalPrice(n, edges, price, trips)
fmt.Println(result)
}

2023-10-04:用go语言,现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge的更多相关文章

  1. 计算机二级-C语言-程序填空题-190109记录-对二维字符串数组的处理

    //给定程序,函数fun的功能是:求出形参ss所指字符串数组中最长字符串的长度,将其余字符串右边用字符*补齐,使其与最长的字符串等长.ss所指字符串数组中共有M个字符串,且串长<N. //重难点 ...

  2. 子串查询(二维前缀数组) 2018"百度之星"程序设计大赛 - 资格赛

    子串查询 Time Limit: 3500/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Subm ...

  3. 【C/C++】二维数组的传参的方法/二维字符数组的声明,使用,输入,传参

    [问题] 定义了一个子函数,传参的内容是一个二维数组 编译提示错误 因为多维数组作为形参传入时,必须声明除第一位维外的确定值,否则系统无法编译(算不出偏移地址) [二维数组的传参] 方法一:形参为二维 ...

  4. Task 4.4二维环形数组求最大子矩阵之和

    任务: (1)输入一个二维整形数组,数组里有正数也有负数. (2)二维数组首尾相接,象个一条首尾相接带子一样. (3)数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. (4)求所有子数 ...

  5. 分配一维动态数组or 二维动态数组的方法以及学习 new 方法or vector

    先来个开胃菜 // 使用new动态分配存储空间 #include<iostream> using std::cout; int main() { // 第1种方式 int *a=new i ...

  6. PHP二维关联数组的遍历方式

    采用foreach循环对二维索引数组进行遍历,相对来讲速度更快,效率更高,foreach循环是PHP中专门用来循环数组的.实例也相对简单,多加练习,想清楚程序运行逻辑即可. <?php $arr ...

  7. C++ 指针二维数组, C++二维指针数组笔记

    C++ 二维动态数组 一. 已知第一维 #include <iostream> using namespace std; int main(int argc, char const *ar ...

  8. 二维字符数组利用gets()函数输入

    举例: ][]; ;i<;i++) gets(a[i]); a是二维字符数组的数组名,相当于一维数组的指针, 所以a[i]就相当于指向第i个数组的指针,类型就相当于char *,相当于字符串.

  9. go 动态数组 二维动态数组

    go使用动态数组还有点麻烦,比python麻烦一点,需要先定义. 动态数组申明 var dynaArr []string 动态数组添加成员 dynaArr = append(dynaArr, &quo ...

  10. 求一个二维整数数组最大子数组之和,时间复杂度为N^2

    本随笔只由于时间原因,我就只写写思想了 二维数组最大子数组之和,可以  引用  一维最大子数组之和 的思想一维最大子数组之和 的思想,在本博客上有,这里就不做多的介绍了 我们有一个最初的二维数组a[n ...

随机推荐

  1. Java CAS:AtomicInteger、AtomicReference、AtomicStampedReference

    Java CAS:AtomicInteger.AtomicReference.AtomicStampedReference 什么是CAS? 什么是CAS? 即比较并替换,实现并发算法时常用到的一种技术 ...

  2. Elasticsearch 6.x 配置search-guard 插件

    前言   es之前版本一直无用户验证功能,不过官方有提供一x-pack,但是问题是付费.在es的6.3.2版本中,已经集成了x-pack,虽然es团队已经对x-pack开源,但是在该版本中如果需要使用 ...

  3. 7. 特殊SQL的执行

    1. 模糊查询 ‍ 演示代码: /** * 测试模糊查询 * @param mohu * @return */ List<User> testMohu(@Param("mohu& ...

  4. 【python基础】文件-初识文件

    文本文件可存储的数据量是非常多的.每当需要分析或修改存储在文件中的信息时,首先就是读取文件到内存中,为此可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取. 1.读取文件 1.1读取整个文件 ...

  5. 计算机网络那些事之 MTU 篇 pt.2

    哈喽大家好,我是咸鱼 在<计算机网络那些事之 MTU 篇 >中,咸鱼跟大家介绍了 MTU 是指数据链路层能够传输的最大数据帧的大小 如果发送的数据大于 MTU,则就会进行分片操作(Frag ...

  6. Task Execution and Scheduling In SpringBoot

    开天辟地 Task Execution and Scheduling In the absence of an Executor bean in the context, Spring Boot au ...

  7. 前端Vue组件之仿京东拼多多领取优惠券弹出框popup 可用于电商商品详情领券场景使用

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身.通过组件化开发,可以有效 ...

  8. 基于JavaFX的扫雷游戏实现(四)——排行榜

      这期看标题已经能猜到了,主要讲的是成绩排行功能,还有对应的文件读写.那么废话不多说,让我们有请今天的主角...的设计稿:   那么主角是何方神圣呢?当然是图中的大框框--TableView.关于这 ...

  9. pod setup 慢 的问题

    由于更换了硬盘,重装了系统,需要重新配置环境,发现现在安装cocapods比之前坑更深了, 装环境时遇到pod setup才几kb的下载速度(即使用梯子也是巨慢),实在是没法用在网上尝试了各种方法,但 ...

  10. 从零玩转系列之SpringBoot3-核心原理

    一.简介 1.前置知识 ● Java17 ● Spring.SpringMVC.MyBatis ● Maven.IDEA 2.环境要求 环境&工具 版本(or later) SpringBoo ...