class Solution {
public:
long long maximumScoreAfterOperations(vector<vector<int>>& edges, vector<int>& values) {
}
};
2925. 在树上执行操作以后得到的最大分数
有一棵 n
个节点的无向树,节点编号为 0
到 n - 1
,根节点编号为 0
。给你一个长度为 n - 1
的二维整数数组 edges
表示这棵树,其中 edges[i] = [ai, bi]
表示树中节点 ai
和 bi
有一条边。
同时给你一个长度为 n
下标从 0 开始的整数数组 values
,其中 values[i]
表示第 i
个节点的值。
一开始你的分数为 0
,每次操作中,你将执行:
i
。values[i]
加入你的分数。values[i]
变为 0
。如果从根节点出发,到任意叶子节点经过的路径上的节点值之和都不等于 0 ,那么我们称这棵树是 健康的 。
你可以对这棵树执行任意次操作,但要求执行完所有操作以后树是 健康的 ,请你返回你可以获得的 最大分数 。
示例 1:
输入:edges = [[0,1],[0,2],[0,3],[2,4],[4,5]], values = [5,2,5,2,1,1] 输出:11 解释:我们可以选择节点 1 ,2 ,3 ,4 和 5 。根节点的值是非 0 的。所以从根出发到任意叶子节点路径上节点值之和都不为 0 。所以树是健康的。你的得分之和为 values[1] + values[2] + values[3] + values[4] + values[5] = 11 。 11 是你对树执行任意次操作以后可以获得的最大得分之和。
示例 2:
输入:edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]], values = [20,10,9,7,4,3,5] 输出:40 解释:我们选择节点 0 ,2 ,3 和 4 。 - 从 0 到 4 的节点值之和为 10 。 - 从 0 到 3 的节点值之和为 10 。 - 从 0 到 5 的节点值之和为 3 。 - 从 0 到 6 的节点值之和为 5 。 所以树是健康的。你的得分之和为 values[0] + values[2] + values[3] + values[4] = 40 。 40 是你对树执行任意次操作以后可以获得的最大得分之和。
提示:
2 <= n <= 2 * 104
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
values.length == n
1 <= values[i] <= 109
edges
构成一棵合法的树。原站题解
golang 解法, 执行用时: 148 ms, 内存消耗: 8.9 MB, 提交时间: 2023-11-06 22:43:24
func maximumScoreAfterOperations(edges [][]int, values []int) int64 { g := make([][]int, len(values)) g[0] = append(g[0], -1) // 避免误把根节点当作叶子 for _, e := range edges { x, y := e[0], e[1] g[x] = append(g[x], y) g[y] = append(g[y], x) } total := 0 // dfs(x, fa) 计算以 x 为根的子树是健康时,失去的最小分数 var dfs func(int, int) int dfs = func(x, fa int) int { total += values[x] if len(g[x]) == 1 { // x 是叶子 return values[x] } loss := 0 // 第二种情况 for _, y := range g[x] { if y != fa { loss += dfs(y, x) // 计算以 y 为根的子树是健康时,失去的最小分数 } } return min(values[x], loss) // 两种情况取最小值 } return int64(total - dfs(0, -1)) }
cpp 解法, 执行用时: 316 ms, 内存消耗: 156.3 MB, 提交时间: 2023-11-06 22:43:04
class Solution { public: long long maximumScoreAfterOperations(vector<vector<int>> &edges, vector<int> &values) { vector<vector<int>> g(values.size()); g[0].push_back(-1); // 避免误把根节点当作叶子 for (auto &e: edges) { int x = e[0], y = e[1]; g[x].push_back(y); g[y].push_back(x); } // dfs(x, fa) 计算以 x 为根的子树是健康时,失去的最小分数 function<long long(int, int)> dfs = [&](int x, int fa) -> long long { if (g[x].size() == 1) { // x 是叶子 return values[x]; } long long loss = 0; // 第二种情况 for (int y: g[x]) { if (y != fa) { loss += dfs(y, x); // 计算以 y 为根的子树是健康时,失去的最小分数 } } return min((long long) values[x], loss); // 两种情况取最小值 }; return accumulate(values.begin(), values.end(), 0LL) - dfs(0, -1); } };
java 解法, 执行用时: 21 ms, 内存消耗: 55.3 MB, 提交时间: 2023-11-06 22:40:33
class Solution { public long maximumScoreAfterOperations(int[][] edges, int[] values) { List<Integer>[] g = new ArrayList[values.length]; Arrays.setAll(g, e -> new ArrayList<>()); g[0].add(-1); // 避免误把根节点当作叶子 for (int[] e : edges) { int x = e[0], y = e[1]; g[x].add(y); g[y].add(x); } // 先把所有分数加入答案 long ans = 0; for (int v : values) { ans += v; } return ans - dfs(0, -1, g, values); } // dfs(x) 计算以 x 为根的子树是健康时,失去的最小分数 private long dfs(int x, int fa, List<Integer>[] g, int[] values) { if (g[x].size() == 1) { // x 是叶子 return values[x]; } long loss = 0; // 第二种情况 for (int y : g[x]) { if (y != fa) { loss += dfs(y, x, g, values); // 计算以 y 为根的子树是健康时,失去的最小分数 } } return Math.min(values[x], loss); // 两种情况取最小值 } }
python3 解法, 执行用时: 204 ms, 内存消耗: 47.9 MB, 提交时间: 2023-11-06 22:40:13
class Solution: def maximumScoreAfterOperations(self, edges: List[List[int]], values: List[int]) -> int: g = [[] for _ in values] g[0].append(-1) # 避免误把根节点当作叶子 for x, y in edges: g[x].append(y) g[y].append(x) # dfs(x, fa) 计算以 x 为根的子树是健康时,失去的最小分数 def dfs(x: int, fa: int) -> int: if len(g[x]) == 1: # x 是叶子 return values[x] loss = 0 # 第二种情况 for y in g[x]: if y != fa: loss += dfs(y, x) # 计算以 y 为根的子树是健康时,失去的最小分数 return min(values[x], loss) # 两种情况取最小值 return sum(values) - dfs(0, -1)