Maximum Score After Applying Operations on a Tree
MEDIUMDescription
There is an undirected tree with n nodes labeled from 0 to n - 1, and rooted at node 0. You are given 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.
You are also given a 0-indexed integer array values of length n, where values[i] is the value associated with the ith node.
You start with a score of 0. In one operation, you can:
- Pick any node
i. - Add
values[i]to your score. - Set
values[i]to0.
A tree is healthy if the sum of values on the path from the root to any leaf node is different than zero.
Return the maximum score you can obtain after performing these operations on the tree any number of times so that it remains healthy.
Example 1:
Input: edges = [[0,1],[0,2],[0,3],[2,4],[4,5]], values = [5,2,5,2,1,1] Output: 11 Explanation: We can choose nodes 1, 2, 3, 4, and 5. The value of the root is non-zero. Hence, the sum of values on the path from the root to any leaf is different than zero. Therefore, the tree is healthy and the score is values[1] + values[2] + values[3] + values[4] + values[5] = 11. It can be shown that 11 is the maximum score obtainable after any number of operations on the tree.
Example 2:
Input: edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]], values = [20,10,9,7,4,3,5] Output: 40 Explanation: We can choose nodes 0, 2, 3, and 4. - The sum of values on the path from 0 to 4 is equal to 10. - The sum of values on the path from 0 to 3 is equal to 10. - The sum of values on the path from 0 to 5 is equal to 3. - The sum of values on the path from 0 to 6 is equal to 5. Therefore, the tree is healthy and the score is values[0] + values[2] + values[3] + values[4] = 40. It can be shown that 40 is the maximum score obtainable after any number of operations on the tree.
Constraints:
2 <= n <= 2 * 104edges.length == n - 1edges[i].length == 20 <= ai, bi < nvalues.length == n1 <= values[i] <= 109- The input is generated such that
edgesrepresents a valid tree.
Approaches
Checkout 2 different approaches to solve Maximum Score After Applying Operations on a Tree. Click on different approaches to view the approach and algorithm in detail.
Dynamic Programming on Tree
A more efficient approach uses dynamic programming on the tree. The key insight is to reframe the problem: instead of maximizing the score of nodes we take, we can minimize the sum of values of nodes we don't take. The 'healthy' condition requires that on every path from the root to a leaf, at least one node's value must be left untouched.
This structure lends itself to a post-order traversal (DFS) where for each node, we compute the minimum value that must be left in its subtree to satisfy the healthy condition for all paths starting from that node. This avoids re-computation and explores the decision space efficiently.
Algorithm
- The problem of maximizing the score of taken nodes is equivalent to minimizing the sum of values of nodes that are left untouched, subject to the 'healthy' constraint.
- The healthy constraint means that for every root-to-leaf path, at least one node must be left untouched.
- We can define a function,
minCost(u), which represents the minimum sum of values we must leave in the subtree rooted atuto ensure all paths fromuto leaves within its subtree are 'healthy'. - We can compute
minCost(u)using a post-order traversal (DFS). - Base Case: For a leaf node
u, the only path in its subtree is the node itself. To make this path healthy, we must leavevalues[u]. So,minCost(u) = values[u]. - Recursive Step: For an internal node
u, we have two choices:- Leave
values[u]. The cost isvalues[u]. This satisfies the condition for all paths fromudownwards. - Take
values[u]. Now, for each childvofu, we must satisfy the condition withinv's subtree. The total cost for this choice is the sum ofminCost(v)for all childrenv.
- Leave
- The recurrence relation is
minCost(u) = min(values[u], sum(minCost(v) for v in children(u))). - First, calculate the total sum of all values in the tree.
- Then, run the DFS from the root (node 0) to compute
minCost(0). - The final maximum score is
totalSum - minCost(0).
We can solve this problem optimally in linear time using a single DFS traversal. Let's define dfs(u, p) as a function that computes the minimum sum of values that must be left in the subtree rooted at u (with p being its parent) to ensure that any path from u to a leaf in its subtree has a non-zero sum.
- For a leaf node
u: The only path is the node itself. We must leave its value. So,dfs(u, p)returnsvalues[u]. - For an internal node
u: We have two options:- Leave
values[u]: The cost incurred isvalues[u]. Sinceuis on every path starting from itself, this single choice makes all paths fromuto its descendant leaves healthy. - Take
values[u]: We don't leavevalues[u]. To maintain the healthy property, we must now ensure it's satisfied in the subtrees of its children. Since the subtrees are independent, we must pay the minimum cost for each child's subtree. The total cost is the sum of the results ofdfs(v, u)for all childrenvofu.
- Leave
We choose the option with the minimum cost. Thus, for an internal node u, dfs(u, p) = min(values[u], sum(dfs(v, u))) for all children v.
The final answer is the total sum of all values in the tree minus the minimum cost for the entire tree, which is dfs(0, -1).
import java.util.ArrayList;
import java.util.List;
class Solution {
private List<List<Integer>> adj;
private int[] values;
public long maximumScoreAfterOperations(int[][] edges, int[] values) {
int n = values.length;
this.adj = new ArrayList<>();
for (int i = 0; i < n; i++) {
adj.add(new ArrayList<>());
}
for (int[] edge : edges) {
adj.get(edge[0]).add(edge[1]);
adj.get(edge[1]).add(edge[0]);
}
this.values = values;
long totalSum = 0;
for (int value : values) {
totalSum += value;
}
long minUnpickedSum = dfs(0, -1);
return totalSum - minUnpickedSum;
}
/**
* Computes the minimum sum of values to leave in the subtree of u
* to make all paths from u to its leaves healthy.
*/
private long dfs(int u, int p) {
long childrenSubtreeMinSum = 0;
boolean isLeaf = true;
for (int v : adj.get(u)) {
if (v != p) {
isLeaf = false;
childrenSubtreeMinSum += dfs(v, u);
}
}
// Base case: if the node is a leaf in the rooted tree traversal.
if (isLeaf) {
return (long)values[u];
}
// Recursive step: for an internal node, choose the minimum of two options.
return Math.min((long)values[u], childrenSubtreeMinSum);
}
}
Complexity Analysis
Pros and Cons
- Optimal time complexity of O(n).
- Efficiently solves the problem for large inputs.
- Elegant solution based on a clear recurrence relation.
- Requires recognizing the problem can be transformed and solved with tree DP.
- Slightly more complex to implement than a naive brute-force approach.
Code Solutions
Checking out 3 solutions in different languages for Maximum Score After Applying Operations on a Tree. Click on different languages to view the code.
class Solution {
private
List<Integer>[] g;
private
int[] values;
public
long maximumScoreAfterOperations(int[][] edges, int[] values) {
int n = values.length;
g = new List[n];
this.values = values;
Arrays.setAll(g, k->new ArrayList<>());
for (var e : edges) {
int a = e[0], b = e[1];
g[a].add(b);
g[b].add(a);
}
return dfs(0, -1)[1];
}
private
long[] dfs(int i, int fa) {
long a = 0, b = 0;
boolean leaf = true;
for (int j : g[i]) {
if (j != fa) {
leaf = false;
var t = dfs(j, i);
a += t[0];
b += t[1];
}
}
if (leaf) {
return new long[]{values[i], 0};
}
return new long[]{values[i] + a, Math.max(values[i] + b, a)};
}
}
Video Solution
Watch the video walkthrough for Maximum Score After Applying Operations on a Tree
Similar Questions
5 related questions you might find useful
Algorithms:
Patterns:
Data Structures:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.