首页 > 其他分享 >2023-10-04:用go语言,现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge

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

时间:2023-10-20 10:07:30浏览次数:65  
标签:10 cur 04 int graph 整数 trips uf 节点


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_golang


标签:10,cur,04,int,graph,整数,trips,uf,节点
From: https://blog.51cto.com/moonfdd/7946857

相关文章

  • 文心一言 VS 讯飞星火 VS chatgpt (107)-- 算法导论10.1 5题
    五、用go语言,栈插入和删除元素只能在同一端进行,队列的插入操作和删除操作分别在两端进行,与它们不同的,有一种双端队列(deque),其插入和删除操作都可以在两端进行。写出4个时间均为O(1)的过程,分别实现在双端队列的两端插入和删除元素的操作,该队列是用一个数组实现的。文心一言:在Go......
  • 文心一言 VS 讯飞星火 VS chatgpt (104)-- 算法导论10.1 2题
    二、用go语言,说明如何在一个数组A[1…n]中实现两个栈,使得当两个栈的元素个数之和不为n时,两者都不会发生上溢。要求PUSH和POP操作的运行时间为O(1)。文心一言:在这个问题中,我们要在一个数组A[1…n]中实现两个栈,使得当两个栈的元素个数之和不为n时,两者都不会发生上溢。这......
  • 文心一言 VS 讯飞星火 VS chatgpt (103)-- 算法导论10.1 1题
    一、用go语言,仿照图10-1,画图表示依次执行操作PUSH(S,4)、PUSH(S,1)、PUSH(S,3)、POP(S)、PUSH(S,8)和POP(S)每一步的结果,栈S初始为空,存储于数组S[1…6]中。文心一言:图10-1展示了在执行一系列栈操作后栈的状态。我会用文字描述来模仿这个图,因为目前我无法直接绘制图片。栈S初始......
  • 2023-09-30:用go语言,给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1, 每一
    2023-09-30:用go语言,给你一个整数数组nums和一个整数k。nums仅包含0和1,每一次移动,你可以选择相邻两个数字并将它们交换。请你返回使nums中包含k个连续1的最少交换次数。输入:nums=[1,0,0,1,0,1],k=2。输出:1。答案2023-09-30:步骤描述:1.定义一个函数minMoves......
  • 10.20算法
    位1的个数编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为'1'的个数(也被称为汉明重量)。 提示:请注意,在某些语言(如Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号......
  • 20231018 NOIP 模拟赛
    时间安排7:50~8:00看题,只会A。8:00~8:10写完A。8:10~9:00推式子+写40pts,少乘了一个\(n-i+1\)调了半天。9:00~9:01看了一眼C的式子,猜一手结论。9:01~10:21觉得可以换根,写个暴力\(dp\)。10:09会了50pts,换下根就行了,10:21调出来了。10:21~10:50给B和C加......
  • 20231019
    20231019NOIP#24总结时间安排7:40~8:10看题,\(A,B,C\)各会一点,\(D\)没想法。8:10~8:30写\(A\)的暴力。8:30~9:00写\(C\)的暴力。9:00~9:30会\(A\)写了。9:30~11:00马上看出了\(B\)的做法,但是可能太久没写了写挂了,调了一个小时。11:00~11:40\(D\)写半天看......
  • 2023-10-19 闲话
    如果你非常感兴趣这些内容,请你仔细辨认【数据删除】的含义。你猜猜第二个自然段是怎么造出来的。我好像也不是很清楚。初三有一个课间我从教室走出去上厕所,快上课了走得很急,拐角的地方【数据删除】撞到了我,随便说了两句你没事吧。然后我去厕所【数据删除】回教室。因为是同桌所......
  • 10.19
    今天做了什么:今天满课.上午上的uml和体育课下午上的数据结构和离散数学,上午的uml上课老师带着我们学习,交互建模并且说了一下关于uml建模选题的建议.然后就是上体育课分组练习教了我们扣球接着就是下午上的数据结构讲了关于树二叉树,森林的转换和关系,还有关于查找父和子的方法......
  • 2023.10.19
    今天上课验收了ERP页面原型,大家验收面临的问题大多是对流程的不清楚,首先是依据订单再生产,而不是有的人认为的看见仓库数量再去打印订单。也是我自己的问题,上课时老师也为我们提供了账号和密码,我们登录上去看一下流程,做的原型页面都不会这么差。上课老师留的课后作业有没有去看,自己......