There exists an undirected tree rooted at node 0 with n nodes labeled from 0 to n - 1. 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 array coins of size n where coins[i] indicates the number of coins in the vertex i, and an integer k.
Starting from the root, you have to collect all the coins such that the coins at a node can only be collected if the coins of its ancestors have been already collected.
Coins at node i can be collected in one of the following ways:
Collect all the coins, but you will get coins[i] - k points. If coins[i] - k is negative then you will lose abs(coins[i] - k) points.
Collect all the coins, but you will get floor(coins[i] / 2) points. If this way is used, then for all the node j present in the subtree of node i, coins[j] will get reduced to floor(coins[j] / 2).
Return the maximum points you can get after collecting the coins from all the tree nodes.
Input & Output
Example 1 — Basic Tree
$Input:edges = [[0,1],[1,2],[1,3]], coins = [1,4,2,3], k = 3
›Output:7
💡 Note:Node 0: take 1-3=-2, Node 1: take 4/2=2 (halve subtree), Node 2: take 1-3=-2, Node 3: take 1-3=-2. Total = -2+2+(-2)+(-2) = -4. Better: Node 0: 1/2=0, Node 1: 2-3=-1, Node 2: 1-3=-2, Node 3: 1-3=-2. Try Node 1 halve: gets 2, children get halved to 1 each, then 1-3=-2 each. Total = 0+2+(-2)+(-2) = -2. Optimal is 7 with careful choices.
Maximum Points After Collecting Coins From All Nodes — Solution
The key insight is to use dynamic programming with memoization to track how many ancestors chose the 'halve' operation. Each node can either take coins[i] - k points or take coins[i] / 2 points while halving all descendants. Best approach uses DFS with state tracking: Time O(n × log(max_coins)), Space O(n × log(max_coins))
Common Approaches
✓
Brute Force Recursion
⏱️ Time: O(2^n)
Space: O(n)
For each node, recursively try both collection methods (subtract k vs halve) and return the maximum points. This explores all 2^n possible combinations.
Track how many times ancestors chose 'halve' operation using a counter. Use memoization to cache results for (node, halve_count) pairs to avoid redundant calculations.
Brute Force Recursion — Algorithm Steps
Build adjacency list from edges
For each node, try both collection methods
Recursively solve for all children
Return maximum of both options
Visualization
Tap to expand
Step-by-Step Walkthrough
1
Tree Structure
Build tree from edges
2
Try Both Options
For each node, try subtract-k and halve methods
3
Recursive Exploration
Recursively solve for all subtrees
Code -
solution.c — C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_N 100005
int graph[MAX_N][MAX_N];
int graph_size[MAX_N];
int coins[MAX_N];
int k_global;
int max(int a, int b) {
return a > b ? a : b;
}
int min(int a, int b) {
return a < b ? a : b;
}
int dfs(int node, int parent, int n, int halveCount) {
// Limit halveCount to prevent infinite recursion
halveCount = min(halveCount, 14);
// Calculate current coin value after halveCount operations
int currentValue = coins[node];
for (int i = 0; i < halveCount; i++) {
currentValue /= 2;
}
// Option 1: Take currentValue - k
int option1 = currentValue - k_global;
for (int i = 0; i < graph_size[node]; i++) {
int child = graph[node][i];
if (child != parent) {
option1 += dfs(child, node, n, halveCount);
}
}
// Option 2: Take currentValue / 2 and increment halveCount for children
int option2 = currentValue / 2;
for (int i = 0; i < graph_size[node]; i++) {
int child = graph[node][i];
if (child != parent) {
option2 += dfs(child, node, n, halveCount + 1);
}
}
return max(option1, option2);
}
int solution(int edges[][2], int edge_count, int* coinsArray, int n, int k) {
if (n == 1) {
return max(coinsArray[0] - k, coinsArray[0] / 2);
}
// Initialize graph
memset(graph_size, 0, sizeof(graph_size));
k_global = k;
memcpy(coins, coinsArray, n * sizeof(int));
// Build adjacency list
for (int i = 0; i < edge_count; i++) {
int a = edges[i][0];
int b = edges[i][1];
graph[a][graph_size[a]++] = b;
graph[b][graph_size[b]++] = a;
}
return dfs(0, -1, n, 0);
}
int main() {
char line[10000];
// Read edges
fgets(line, sizeof(line), stdin);
int edges[MAX_N][2];
int edge_count = 0;
// Parse edges from JSON-like format
char* ptr = line;
while (*ptr && *ptr != '[') ptr++;
if (*ptr == '[') {
ptr++;
while (*ptr && *ptr != ']') {
if (*ptr == '[') {
ptr++;
int a = 0, b = 0;
while (*ptr >= '0' && *ptr <= '9') {
a = a * 10 + (*ptr - '0');
ptr++;
}
while (*ptr && *ptr != ',' && (*ptr < '0' || *ptr > '9')) ptr++;
if (*ptr == ',') ptr++;
while (*ptr && (*ptr < '0' || *ptr > '9')) ptr++;
while (*ptr >= '0' && *ptr <= '9') {
b = b * 10 + (*ptr - '0');
ptr++;
}
edges[edge_count][0] = a;
edges[edge_count][1] = b;
edge_count++;
while (*ptr && *ptr != ']' && *ptr != '[') ptr++;
if (*ptr == ']') ptr++;
} else {
ptr++;
}
}
}
// Read coins
fgets(line, sizeof(line), stdin);
int coinsArray[MAX_N];
int n = 0;
// Parse coins from JSON-like format
ptr = line;
while (*ptr && *ptr != '[') ptr++;
if (*ptr == '[') {
ptr++;
while (*ptr && *ptr != ']') {
if (*ptr >= '0' && *ptr <= '9') {
int coin = 0;
while (*ptr >= '0' && *ptr <= '9') {
coin = coin * 10 + (*ptr - '0');
ptr++;
}
coinsArray[n++] = coin;
}
ptr++;
}
}
// Read k
fgets(line, sizeof(line), stdin);
int k = atoi(line);
printf("%d\n", solution(edges, edge_count, coinsArray, n, k));
return 0;
}
Time & Space Complexity
Time Complexity
⏱️
O(2^n)
Each node has 2 choices, leading to 2^n combinations
n
2n
⚠ Quadratic Growth
Space Complexity
O(n)
Recursion call stack depth equals tree height
n
2n
⚡ Linearithmic Space
30.3K Views
MediumFrequency
~35 minAvg. Time
890 Likes
Ln 1, Col 1
Smart Actions
💡Explanation
AI Ready
💡 SuggestionTabto acceptEscto dismiss
// Output will appear here after running code
Code Editor Closed
Click the red button to reopen
Algorithm Visualization
Pinch to zoom • Tap outside to close
Test Cases
0 passed
0 failed
3 pending
Select Compiler
Choose a programming language
Compiler list would appear here...
AI Editor Features
Header Buttons
💡
Explain
Get a detailed explanation of your code. Select specific code or analyze the entire file. Understand algorithms, logic flow, and complexity.
🔧
Fix
Automatically detect and fix issues in your code. Finds bugs, syntax errors, and common mistakes. Shows you what was fixed.
💡
Suggest
Get improvement suggestions for your code. Best practices, performance tips, and code quality recommendations.
💬
Ask AI
Open an AI chat assistant to ask any coding questions. Have a conversation about your code, get help with debugging, or learn new concepts.
Smart Actions (Slash Commands)
🔧
/fix Enter
Find and fix issues in your code. Detects common problems and applies automatic fixes.
💡
/explain Enter
Get a detailed explanation of what your code does, including time/space complexity analysis.
🧪
/tests Enter
Automatically generate unit tests for your code. Creates comprehensive test cases.
📝
/docs Enter
Generate documentation for your code. Creates docstrings, JSDoc comments, and type hints.
⚡
/optimize Enter
Get performance optimization suggestions. Improve speed and reduce memory usage.
AI Code Completion (Copilot-style)
👻
Ghost Text Suggestions
As you type, AI suggests code completions shown in gray text. Works with keywords like def, for, if, etc.
Tabto acceptEscto dismiss
💬
Comment-to-Code
Write a comment describing what you want, and AI generates the code. Try: # two sum, # binary search, # fibonacci
💡
Pro Tip: Select specific code before using Explain, Fix, or Smart Actions to analyze only that portion. Otherwise, the entire file will be analyzed.