leetcode
leetcode 2701 ~ 2750
最小化旅行的价格总和

最小化旅行的价格总和

难度:

标签:

题目描述

There exists an undirected and unrooted tree with n nodes indexed from 0 to n - 1. You are given the integer n and a 2D integer array edges of length n - 1, where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree.

Each node has an associated price. You are given an integer array price, where price[i] is the price of the ith node.

The price sum of a given path is the sum of the prices of all nodes lying on that path.

Additionally, you are given a 2D integer array trips, where trips[i] = [starti, endi] indicates that you start the ith trip from the node starti and travel to the node endi by any path you like.

Before performing your first trip, you can choose some non-adjacent nodes and halve the prices.

Return the minimum total price sum to perform all the given trips.

 

Example 1:

Input: n = 4, edges = [[0,1],[1,2],[1,3]], price = [2,2,10,6], trips = [[0,3],[2,1],[2,3]]
Output: 23
Explanation: The diagram above denotes the tree after rooting it at node 2. The first part shows the initial tree and the second part shows the tree after choosing nodes 0, 2, and 3, and making their price half.
For the 1st trip, we choose path [0,1,3]. The price sum of that path is 1 + 2 + 3 = 6.
For the 2nd trip, we choose path [2,1]. The price sum of that path is 2 + 5 = 7.
For the 3rd trip, we choose path [2,1,3]. The price sum of that path is 5 + 2 + 3 = 10.
The total price sum of all trips is 6 + 7 + 10 = 23.
It can be proven, that 23 is the minimum answer that we can achieve.

Example 2:

Input: n = 2, edges = [[0,1]], price = [2,2], trips = [[0,0]]
Output: 1
Explanation: The diagram above denotes the tree after rooting it at node 0. The first part shows the initial tree and the second part shows the tree after choosing node 0, and making its price half.
For the 1st trip, we choose path [0]. The price sum of that path is 1.
The total price sum of all trips is 1. It can be proven, that 1 is the minimum answer that we can achieve.

 

Constraints:

  • 1 <= n <= 50
  • edges.length == n - 1
  • 0 <= ai, bi <= n - 1
  • edges represents a valid tree.
  • price.length == n
  • price[i] is an even integer.
  • 1 <= price[i] <= 1000
  • 1 <= trips.length <= 100
  • 0 <= starti, endi <= n - 1

代码结果

运行时间: 53 ms, 内存: 16.4 MB


解释

方法:

该题解通过构建无向图,使用DFS确定树的层级和父子关系,并计算出所有行程中各节点被访问的频率。使用动态规划(dp)决策是否对节点价格减半以最小化旅行成本。具体步骤如下:1. 使用DFS建立树的层级结构,以便后续快速查询两节点的最近公共祖先(LCA)。2. 根据每次旅行的起止点,利用LCA和父节点信息计算出行程中的节点访问频率。3. 利用动态规划,针对每个节点决定是否减半其价格,以最小化总花费。dp函数计算两种情况:不减半和减半的总费用,并递归计算子节点的最优解。4. 最终,通过比较不减半和至少一个节点减半的总费用,得出最小的旅行总费用。

时间复杂度:

O(n + t)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在构建树的层级和父子关系时选择使用DFS而不是BFS?
在构建树的层级和父子关系时选择使用DFS(深度优先搜索)而不是BFS(广度优先搜索),主要是因为DFS可以在遍历过程中直接递归地设置每个节点的父节点和层级,而无需额外的数据结构来维护队列。此外,DFS可以在一次遍历中直接深入到树的最深层,然后逐层返回,递归地构建整个树结构,这在逻辑上更为直接和简洁。相比之下,BFS需要使用队列来维护当前层的所有节点,对于只是建立层级和父子关系的需求可能显得更复杂。
🦆
如何确保函数`LCA(a, b)`在所有情况下都能正确返回两个节点的最近公共祖先?
函数`LCA(a, b)`(最近公共祖先)能够在所有情况下正确返回两个节点的最近公共祖先,是通过保证两个节点在同一层级上相遇。具体步骤如下:首先,如果a和b在不同层级,则将更深的节点向上移动,直到两节点处于同一层级。然后,如果a和b不相同,同时将两节点向它们的父节点移动,直到它们相遇。这种方法确保了无论树的结构如何,都可以通过逐级向上追溯找到a和b的公共祖先。
🦆
动态规划部分中,`dp(i, p)`函数返回的两个值分别代表什么意义?
`dp(i, p)`函数在动态规划部分中返回两个值,分别代表以下意义:第一个值(`res0`)代表在节点i及其子树中,如果节点i的价格不减半时,从节点i开始的所有子树的最优总花费。第二个值(`res1`)则代表在节点i及其子树中,如果节点i的价格减半时,从节点i开始的所有子树的最优总花费。这两个值通过递归计算i的所有子节点的最优解,并结合i节点本身的费用状态(减半或不减半),来决定整棵子树的最优花费策略。

相关问题