LeetCode 题解工作台
收集所有金币可获得的最大积分
有一棵由 n 个节点组成的无向树,以 0 为根节点,节点编号从 0 到 n - 1 。给你一个长度为 n - 1 的二维 整数 数组 edges ,其中 edges[i] = [a i , b i ] 表示在树上的节点 a i 和 b i 之间存在一条边。另给你一个下标从 0 开始、长度为 n 的数…
6
题型
5
代码语言
3
相关题
当前训练重点
困难 · 二分·树·traversal
答案摘要
我们先根据题目给定的边构建图 ,其中 表示节点 的所有邻接节点。然后我们可以使用记忆化搜索的方法求解本题。 我们设计一个函数 $dfs(i, fa, j)$,表示当前节点为 ,父节点为 ,当前节点的金币数需要右移 位,所能获得的最大积分。
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 二分·树·traversal 题型思路
题目描述
有一棵由 n 个节点组成的无向树,以 0 为根节点,节点编号从 0 到 n - 1 。给你一个长度为 n - 1 的二维 整数 数组 edges ,其中 edges[i] = [ai, bi] 表示在树上的节点 ai 和 bi 之间存在一条边。另给你一个下标从 0 开始、长度为 n 的数组 coins 和一个整数 k ,其中 coins[i] 表示节点 i 处的金币数量。
从根节点开始,你必须收集所有金币。要想收集节点上的金币,必须先收集该节点的祖先节点上的金币。
节点 i 上的金币可以用下述方法之一进行收集:
- 收集所有金币,得到共计
coins[i] - k点积分。如果coins[i] - k是负数,你将会失去abs(coins[i] - k)点积分。 - 收集所有金币,得到共计
floor(coins[i] / 2)点积分。如果采用这种方法,节点i子树中所有节点j的金币数coins[j]将会减少至floor(coins[j] / 2)。
返回收集 所有 树节点的金币之后可以获得的最大积分。
示例 1:
输入:edges = [[0,1],[1,2],[2,3]], coins = [10,10,3,3], k = 5 输出:11 解释: 使用第一种方法收集节点 0 上的所有金币。总积分 = 10 - 5 = 5 。 使用第一种方法收集节点 1 上的所有金币。总积分 = 5 + (10 - 5) = 10 。 使用第二种方法收集节点 2 上的所有金币。所以节点 3 上的金币将会变为 floor(3 / 2) = 1 ,总积分 = 10 + floor(3 / 2) = 11 。 使用第二种方法收集节点 3 上的所有金币。总积分 = 11 + floor(1 / 2) = 11. 可以证明收集所有节点上的金币能获得的最大积分是 11 。
示例 2:
输入:edges = [[0,1],[0,2]], coins = [8,4,4], k = 0 输出:16 解释: 使用第一种方法收集所有节点上的金币,因此,总积分 = (8 - 0) + (4 - 0) + (4 - 0) = 16 。
提示:
n == coins.length2 <= n <= 1050 <= coins[i] <= 104edges.length == n - 10 <= edges[i][0], edges[i][1] < n0 <= k <= 104
解题思路
方法一:记忆化搜索
我们先根据题目给定的边构建图 ,其中 表示节点 的所有邻接节点。然后我们可以使用记忆化搜索的方法求解本题。
我们设计一个函数 ,表示当前节点为 ,父节点为 ,当前节点的金币数需要右移 位,所能获得的最大积分。
函数 的执行过程如下:
如果我们使用第一种方法收集当前节点的金币,那么当前节点的积分为 ,然后我们遍历当前节点的所有邻接节点 ,如果 不等于 ,那么我们将 的结果累加到当前节点的积分中。
如果我们使用第二种方法收集当前节点的金币,那么当前节点的积分为 ,然后我们遍历当前节点的所有邻接节点 ,如果 不等于 ,那么我们将 的结果累加到当前节点的积分中。注意,由于题目中给定的 最大值为 ,因此我们最多只能右移 位,就使得 的值为 。
最后,我们返回当前节点使用两种方法中能获得的最大积分。
为了避免重复计算,我们使用记忆化搜索的方法,将 的结果存储到 中,其中 表示当前节点为 ,父节点为 ,当前节点的金币数需要右移 位,所能获得的最大 时间复杂度 ,空间复杂度 。其中 表示 的最大值。
class Solution:
def maximumPoints(self, edges: List[List[int]], coins: List[int], k: int) -> int:
@cache
def dfs(i: int, fa: int, j: int) -> int:
a = (coins[i] >> j) - k
b = coins[i] >> (j + 1)
for c in g[i]:
if c != fa:
a += dfs(c, i, j)
if j < 14:
b += dfs(c, i, j + 1)
return max(a, b)
n = len(coins)
g = [[] for _ in range(n)]
for a, b in edges:
g[a].append(b)
g[b].append(a)
ans = dfs(0, -1, 0)
dfs.cache_clear()
return ans
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | Depends on the final approach |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
Does the candidate efficiently use dynamic programming and memoization to avoid redundant calculations?
- question_mark
Can the candidate demonstrate understanding of how state tracking helps maximize the points for this problem?
- question_mark
Does the candidate effectively use depth-first search for tree traversal while considering the two coin collection methods?
常见陷阱
外企场景- error
Failing to implement the second coin collection method properly and not adjusting the remaining coins correctly.
- error
Not using memoization or dynamic programming to store intermediate results, leading to inefficient solutions.
- error
Incorrectly handling the state transitions, especially when switching between the two coin collection methods.
进阶变体
外企场景- arrow_right_alt
Variation 1: What if coins are collected in a specific order or with additional constraints?
- arrow_right_alt
Variation 2: How does the problem change if the tree is not binary, or if nodes can have more than two children?
- arrow_right_alt
Variation 3: What if you had to collect coins starting from multiple nodes simultaneously?