Dynamic Programming(DP) is a technique to solve problems by breaking them down into overlapping sub-problems which follows the optimal substructure. There are various problems using DP like subset sum, knapsack, coin change etc. DP can also be applied on trees to solve some specific problems.
Pre-requisite: DFS
Given a tree with N nodes and N-1 edges, calculate the maximum sum of the node values from root to any of the leaves without re-visiting any node.

Given above is a diagram of a tree with N=14 nodes and N-1=13 edges. The values at node being 3, 2, 1, 10, 1, 3, 9, 1, 5, 3, 4, 5, 9 and 8 respectively for nodes 1, 2, 3, 4….14.
The diagram below shows all the paths from root to leaves :

All the paths are marked by different colors :
Path 1(red, 3-2-1-4) : sum of all node values = 10
Path 2(orange, 3-2-1-5) : sum of all node values = 11
Path 3(yellow, 3-2-3) : sum of all node values = 8
Path 4(green, 3-1-9-9) : sum of all node values = 22
Path 5(violet, 3-1-9-8) : sum of all node values = 21
Path 6(pink, 3-10-1) : sum of all node values = 14
Path 7(blue, 3-10-5) : sum of all node values = 18
Path 8(brown, 3-10-3) : sum of all node values = 16
The answer is 22, as Path 4 has the maximum sum of values of nodes in its path from a root to leaves.
The greedy approach fails in this case. Starting from the root and take 3 from the first level, 10 from the next level and 5 from the third level greedily. Result is path-7 if after following the greedy approach, hence do not apply greedy approach over here.
The problem can be solved using Dynamic Programming on trees. Start memoizing from the leaves and add the maximum of leaves to the root of every sub-tree. At the last step, there will be root and the sub-tree under it, adding the value at node and maximum of sub-tree will give us the maximum sum of the node values from root to any of the leaves.

The diagram above shows how to start from the leaves and add the maximum of leaves of a sub-tree to its root. Move upward and repeat the same procedure of storing the maximum of every sub-tree leaves and adding it to its root. In this example, the maximum of node 11 and 12 is taken to count and then added to node 5 (In this sub-tree, 5 is the root and 11, 12 are its leaves). Similarly, the maximum of node 13 and 14 is taken to count and then added to node 7. Repeat the steps for every sub-tree till we reach the node.
Let DPi be the maximum summation of node values in the path between i and any of its leaves moving downwards. Traverse the tree using DFS traversal. Store the maximum of all the leaves of the sub-tree, and add it to the root of the sub-tree. At the end, DP1 will have the maximum sum of the node values from root to any of the leaves without re-visiting any node.
Below is the implementation of the above idea :
C++
#include <bits/stdc++.h>
using namespace std;
vector< int > dp;
void dfs( int a[], vector< int > v[], int u, int parent)
{
dp[u] = a[u - 1];
int maximum = 0;
for ( int child : v[u]) {
if (child == parent)
continue ;
dfs(a, v, child, u);
maximum = max(maximum, dp[child]);
}
dp[u] += maximum;
}
int maximumValue( int a[], vector< int > v[])
{
dfs(a, v, 1, 0);
return dp[1];
}
int main()
{
int n = 14;
vector< int > v[n + 1];
v[1].push_back(2), v[2].push_back(1);
v[1].push_back(3), v[3].push_back(1);
v[1].push_back(4), v[4].push_back(1);
v[2].push_back(5), v[5].push_back(2);
v[2].push_back(6), v[6].push_back(2);
v[3].push_back(7), v[7].push_back(3);
v[4].push_back(8), v[8].push_back(4);
v[4].push_back(9), v[9].push_back(4);
v[4].push_back(10), v[10].push_back(4);
v[5].push_back(11), v[11].push_back(5);
v[5].push_back(12), v[12].push_back(5);
v[7].push_back(13), v[13].push_back(7);
v[7].push_back(14), v[14].push_back(7);
int a[] = { 3, 2, 1, 10, 1, 3, 9, 1, 5, 3, 4, 5, 9, 8 };
dp = vector< int >(n+1,0);
cout << maximumValue(a, v);
return 0;
}
|
Java
import java.util.Vector;
class GFG
{
static int [] dp = new int [ 100 ];
static void dfs( int [] a, Vector<Integer>[] v,
int u, int parent)
{
dp[u] = a[u - 1 ];
int maximum = 0 ;
for ( int child : v[u])
{
if (child == parent)
continue ;
dfs(a, v, child, u);
maximum = Math.max(maximum, dp[child]);
}
dp[u] += maximum;
}
static int maximumValue( int [] a,
Vector<Integer>[] v)
{
dfs(a, v, 1 , 0 );
return dp[ 1 ];
}
public static void main(String[] args)
{
int n = 14 ;
@SuppressWarnings ( "unchecked" )
Vector<Integer>[] v = new Vector[n + 1 ];
for ( int i = 0 ; i < v.length; i++)
v[i] = new Vector<>();
v[ 1 ].add( 2 ); v[ 2 ].add( 1 );
v[ 1 ].add( 3 ); v[ 3 ].add( 1 );
v[ 1 ].add( 4 ); v[ 4 ].add( 1 );
v[ 2 ].add( 5 ); v[ 5 ].add( 2 );
v[ 2 ].add( 6 ); v[ 6 ].add( 2 );
v[ 3 ].add( 7 ); v[ 7 ].add( 3 );
v[ 4 ].add( 8 ); v[ 8 ].add( 4 );
v[ 4 ].add( 9 ); v[ 9 ].add( 4 );
v[ 4 ].add( 10 ); v[ 10 ].add( 4 );
v[ 5 ].add( 11 ); v[ 11 ].add( 5 );
v[ 5 ].add( 12 ); v[ 12 ].add( 5 );
v[ 7 ].add( 13 ); v[ 13 ].add( 7 );
v[ 7 ].add( 14 ); v[ 14 ].add( 7 );
int a[] = { 3 , 2 , 1 , 10 , 1 , 3 , 9 ,
1 , 5 , 3 , 4 , 5 , 9 , 8 };
System.out.println(maximumValue(a, v));
}
}
|
Python3
dp = [ 0 ] * 100
def dfs(a, v, u, parent):
dp[u] = a[u - 1 ]
maximum = 0
for child in v[u]:
if child = = parent:
continue
dfs(a, v, child, u)
maximum = max (maximum, dp[child])
dp[u] + = maximum
def maximumValue(a, v):
dfs(a, v, 1 , 0 )
return dp[ 1 ]
def main():
n = 14
v = {}
for i in range (n + 1 ):
v[i] = []
v[ 1 ].append( 2 ), v[ 2 ].append( 1 )
v[ 1 ].append( 3 ), v[ 3 ].append( 1 )
v[ 1 ].append( 4 ), v[ 4 ].append( 1 )
v[ 2 ].append( 5 ), v[ 5 ].append( 2 )
v[ 2 ].append( 6 ), v[ 6 ].append( 2 )
v[ 3 ].append( 7 ), v[ 7 ].append( 3 )
v[ 4 ].append( 8 ), v[ 8 ].append( 4 )
v[ 4 ].append( 9 ), v[ 9 ].append( 4 )
v[ 4 ].append( 10 ), v[ 10 ].append( 4 )
v[ 5 ].append( 11 ), v[ 11 ].append( 5 )
v[ 5 ].append( 12 ), v[ 12 ].append( 5 )
v[ 7 ].append( 13 ), v[ 13 ].append( 7 )
v[ 7 ].append( 14 ), v[ 14 ].append( 7 )
a = [ 3 , 2 , 1 , 10 , 1 , 3 , 9 ,
1 , 5 , 3 , 4 , 5 , 9 , 8 ]
print (maximumValue(a, v))
main()
|
C#
using System;
using System.Collections.Generic;
class GFG
{
static int [] dp = new int [100];
static void dfs( int [] a, List< int >[] v,
int u, int parent)
{
dp[u] = a[u - 1];
int maximum = 0;
foreach ( int child in v[u])
{
if (child == parent)
continue ;
dfs(a, v, child, u);
maximum = Math.Max(maximum, dp[child]);
}
dp[u] += maximum;
}
static int maximumValue( int [] a,
List< int >[] v)
{
dfs(a, v, 1, 0);
return dp[1];
}
public static void Main(String[] args)
{
int n = 14;
List< int >[] v = new List< int >[n + 1];
for ( int i = 0; i < v.Length; i++)
v[i] = new List< int >();
v[1].Add(2); v[2].Add(1);
v[1].Add(3); v[3].Add(1);
v[1].Add(4); v[4].Add(1);
v[2].Add(5); v[5].Add(2);
v[2].Add(6); v[6].Add(2);
v[3].Add(7); v[7].Add(3);
v[4].Add(8); v[8].Add(4);
v[4].Add(9); v[9].Add(4);
v[4].Add(10); v[10].Add(4);
v[5].Add(11); v[11].Add(5);
v[5].Add(12); v[12].Add(5);
v[7].Add(13); v[13].Add(7);
v[7].Add(14); v[14].Add(7);
int []a = { 3, 2, 1, 10, 1, 3, 9,
1, 5, 3, 4, 5, 9, 8 };
Console.WriteLine(maximumValue(a, v));
}
}
|
Javascript
<script>
var dp = Array(100).fill(0);
function dfs(a, v, u, parent)
{
dp[u] = a[u - 1];
var maximum = 0;
for ( var child of v[u])
{
if (child == parent)
continue ;
dfs(a, v, child, u);
maximum = Math.max(maximum, dp[child]);
}
dp[u] += maximum;
}
function maximumValue(a, v)
{
dfs(a, v, 1, 0);
return dp[1];
}
var n = 14;
var v = Array.from(Array(n+1), ()=>Array());
for ( var i = 0; i < v.length; i++)
v[i] = [];
v[1].push(2); v[2].push(1);
v[1].push(3); v[3].push(1);
v[1].push(4); v[4].push(1);
v[2].push(5); v[5].push(2);
v[2].push(6); v[6].push(2);
v[3].push(7); v[7].push(3);
v[4].push(8); v[8].push(4);
v[4].push(9); v[9].push(4);
v[4].push(10); v[10].push(4);
v[5].push(11); v[11].push(5);
v[5].push(12); v[12].push(5);
v[7].push(13); v[13].push(7);
v[7].push(14); v[14].push(7);
var a = [3, 2, 1, 10, 1, 3, 9,
1, 5, 3, 4, 5, 9, 8];
document.write(maximumValue(a, v));
</script>
|
Time Complexity: O(N), where N is the number of nodes.
Auxiliary Space: O(N), for creating an additional dp array.