Given a tree with N nodes numbered from 1 to N. Each Node has a value denoting the number of points you get when you visit that Node. Each edge has a length and as soon as you travel through this edge number of points is reduced by the length of the edge, the task is to choose two nodes u and v such that the number of points is maximized at the end of the path from node u to node v.
Note: The number of points should not get negative in between the path from node u to node v.
Examples:
Input:
Output: Maximal Point Path in this case will be 2->1->3 and maximum points at the end of path will be (3-2+1-2+3) = 3
Input:
Output: Maximal Point Path in this case will be 2 -> 4 and maximum points at the end of path will be (3-1+5) = 7
Approach: This problem can be solved optimally using DP with trees concept.
Steps to solve the problem:
- Root the tree at any node say (1).
- For each of the nodes v in the tree, find the maximum points of the path passing through the node v. The answer will be the maximum of this value among all nodes in the tree.
- To calculate this value for each node, maintain a dp array, where dp[v] is the maximal points of the path starting at node v for the subtree rooted at node v. dp[v] for a node v can be calculated from points[v] + max(dp[u] – wv->u ) where u is child of node v, wv->u is length of edge connecting node v and u and points[v] is the number points at the node v.
- Now to find the maximum points of the path passing through the node v we calculate dp[u]-wv->u for each child of v and take the two biggest values from it and add points[v] in it.
- If the maximum value of dp[u]-wv->u is negative then we will take 0 instead of it.
Below is the implementation of the above approach:
C++
// C++ code for the above approach:#include <bits/stdc++.h>using namespace std;Â
class GFG {Â
public:    // Adjacency list to store the edges.    vector<vector<pair<int, int> > > adj;Â
    // To store maximum points of a path    // starting at a node    vector<int> dp;Â
    // Visited vector to keep trackof nodes for    // which dp values has already been calculated    vector<int> vis;Â
    // To store the final answer    int ans = 0;Â
    // Function for visiting every node and    // calculating dp values for each node.    void dfs(int curr_node, vector<int>& points)    {Â
        // Mark the current node as visited so        // that it does not have to be visited again.        vis[curr_node] = 1;Â
        // To store maximum path starting        // at node minus lenght of edge connecting        // that node to current node for each        // child of current node.        vector<int> child_nodes;Â
        // Iterating through each child        // of current node.        for (auto x : adj[curr_node]) {Â
            // To check whether the child has been            // already visited or not            if (!vis[x.first]) {Â
                // Call dfs function for the child                dfs(x.first, points);            }Â
            // Push the value(maximum points path            // starting at this child node minus lenght            // of edge) into the vector            child_nodes.push_back(dp[x.first] - x.second);        }Â
        // Sort the vector in decreasing order        // to pick 2 maximum 2 values.        sort(child_nodes.begin(), child_nodes.end(),             greater<int>());Â
        // max1-to store maximum points path        // starting at child node of current        // node, max2-to store second maximum        // points path starting at child node        // of current node.        int max1 = 0, max2 = 0;        if (child_nodes.size() >= 2) {            max1 = max(max1, child_nodes[0]);            max2 = max(max2, child_nodes[1]);        }        else if (child_nodes.size() >= 1) {            max1 = max(max1, child_nodes[0]);        }Â
        // Calculate maximum points path passing        // through current node.        ans = max(ans, max1 + max2 + points[curr_node]);Â
        // Store maximum points path starting        // at current node in dp[curr_node]        dp[curr_node] = max1 + points[curr_node];    }Â
    // To find maximal points path    int MaxPointPath(int n, vector<int> points,                     vector<vector<int> > edges)    {        adj.resize(n + 1);        dp.resize(n + 1);        vis.resize(n + 1);Â
        // Filling adajency list        for (int i = 0; i < n - 1; i++) {            adj[edges[i][0]].push_back(                { edges[i][1], edges[i][2] });            adj[edges[i][1]].push_back(                { edges[i][0], edges[i][0] });        }Â
        // Calling dfs for node 1        dfs(1, points);Â
        return ans;    }};Â
// Driver codeint main(){Â Â Â Â GFG obj;Â
    // Number of Vertices    int n = 5;Â
    // Points at each node    vector<int> points(n + 1);    points[1] = 6;    points[2] = 3;    points[3] = 2;    points[4] = 5;    points[5] = 0;Â
    // Edges and their lengths    vector<vector<int> > edges{        { 1, 2, 10 }, { 2, 3, 3 }, { 2, 4, 1 }, { 1, 5, 11 }    };Â
    cout << obj.MaxPointPath(n, points, edges);    return 0;} |
Java
import java.util.*;Â
class GFG {    // Adjacency list to store the edges.    List<List<AbstractMap.SimpleEntry<Integer, Integer>>> adj;Â
    // To store maximum points of a path starting at a node    List<Integer> dp;Â
    // Visited vector to keep track of nodes for    // which dp values have already been calculated    List<Integer> vis;Â
    // To store the final answer    int ans = 0;Â
    // Constructor    GFG() {        adj = new ArrayList<>();        dp = new ArrayList<>();        vis = new ArrayList<>();    }Â
    // Function for visiting every node and calculating dp values for each node.    void dfs(int currNode, List<Integer> points) {        // Mark the current node as visited so that it does not have to be visited again.        vis.set(currNode, 1);Â
        // To store maximum path starting at node minus length of edge connecting that node to the current node for each child of the current node.        List<Integer> childNodes = new ArrayList<>();Â
        // Iterating through each child of the current node.        for (AbstractMap.SimpleEntry<Integer, Integer> x : adj.get(currNode)) {            // To check whether the child has been already visited or not            if (vis.get(x.getKey()) == 0) {                // Call dfs function for the child                dfs(x.getKey(), points);            }Â
            // Push the value (maximum points path starting at this child node minus length of the edge) into the vector            childNodes.add(dp.get(x.getKey()) - x.getValue());        }Â
        // Sort the vector in decreasing order to pick 2 maximum 2 values.        Collections.sort(childNodes, Collections.reverseOrder());Â
        // max1 - to store the maximum points path starting at the child node of the current node, max2 - to store the second maximum points path starting at the child node of the current node.        int max1 = 0, max2 = 0;        if (childNodes.size() >= 2) {            max1 = Math.max(max1, childNodes.get(0));            max2 = Math.max(max2, childNodes.get(1));        } else if (childNodes.size() == 1) {            max1 = Math.max(max1, childNodes.get(0));        }Â
        // Calculate maximum points path passing through the current node.        ans = Math.max(ans, max1 + max2 + points.get(currNode));Â
        // Store the maximum points path starting at the current node in dp[currNode]        dp.set(currNode, max1 + points.get(currNode));    }Â
    // To find the maximal points path    int maxPointPath(int n, List<Integer> points, List<List<Integer>> edges) {        for (int i = 0; i <= n; i++) {            adj.add(new ArrayList<>());            dp.add(0);            vis.add(0);        }Â
        // Filling the adjacency list        for (List<Integer> edge : edges) {            adj.get(edge.get(0)).add(new AbstractMap.SimpleEntry<>(edge.get(1), edge.get(2)));            adj.get(edge.get(1)).add(new AbstractMap.SimpleEntry<>(edge.get(0), edge.get(2)));        }Â
        // Calling dfs for node 1        dfs(1, points);Â
        return ans;    }Â
    // Driver code    public static void main(String[] args) {        GFG obj = new GFG();Â
        // Number of Vertices        int n = 5;Â
        // Points at each node        List<Integer> points = new ArrayList<>();        points.add(0); // Adding an extra element at index 0 for simplicity        points.add(6);        points.add(3);        points.add(2);        points.add(5);        points.add(0);Â
        // Edges and their lengths        List<List<Integer>> edges = Arrays.asList(                Arrays.asList(1, 2, 10),                Arrays.asList(2, 3, 3),                Arrays.asList(2, 4, 1),                Arrays.asList(1, 5, 11)        );Â
        System.out.println(obj.maxPointPath(n, points, edges));    }} |
Python3
# Python Code Implementationclass GFG:    def __init__(self):        # Adjacency list to store the edges.        self.adj = []Â
        # To store maximum points of a path         #starting at a node        self.dp = []Â
        # Visited vector to keep track of nodes         #for which dp values has already been calculated        self.vis = []Â
        # To store the final answer        self.ans = 0Â
    # Function for visiting every node and     #calculating dp values for each node.    def dfs(self, curr_node, points):        # Mark the current node as visited so         # that it does not have to be visited again.        self.vis[curr_node] = 1Â
        # To store maximum path starting at node         # minus length of edge connecting that node to         # current node for each child of current node.        child_nodes = []Â
        # Iterating through each child of current node.        for x in self.adj[curr_node]:                       # To check whether the child             # has been already visited or not            if not self.vis[x[0]]:                               # Call dfs function for the child                self.dfs(x[0], points)Â
            # Push the value(maximum points path             # starting at this child node minus length of edge)             # into the vector            child_nodes.append(self.dp[x[0]] - x[1])Â
        # Sort the vector in decreasing         # order to pick 2 maximum values.        child_nodes.sort(reverse=True)Â
        # max1-to store maximum points path starting         # at child node of current node, max2-to store         # second maximum points path starting at         # child node of current node.        max1 = 0        max2 = 0        if len(child_nodes) >= 2:            max1 = max(max1, child_nodes[0])            max2 = max(max2, child_nodes[1])        elif len(child_nodes) >= 1:            max1 = max(max1, child_nodes[0])Â
        # Calculate maximum points path         # passing through current node.        self.ans = max(self.ans, max1 + max2 + points[curr_node])Â
        # Store maximum points path starting         # at current node in dp[curr_node]        self.dp[curr_node] = max1 + points[curr_node]Â
    # To find maximal points path    def MaxPointPath(self, n, points, edges):        self.adj = [[] for _ in range(n + 1)]        self.dp = [0] * (n + 1)        self.vis = [0] * (n + 1)Â
        # Filling adjacency list        for i in range(n - 1):            self.adj[edges[i][0]].append((edges[i][1], edges[i][2]))            self.adj[edges[i][1]].append((edges[i][0], edges[i][2]))Â
        # Calling dfs for node 1        self.dfs(1, points)Â
        return self.ansÂ
Â
# Driver codeif __name__ == "__main__":Â Â Â Â obj = GFG()Â
    # Number of Vertices    n = 5Â
    # Points at each node    points = [0, 6, 3, 2, 5, 0]Â
    # Edges and their lengths    edges = [        [1, 2, 10],        [2, 3, 3],        [2, 4, 1],        [1, 5, 11]    ]Â
    print(obj.MaxPointPath(n, points, edges))Â
# This code is contributed by Sakshi |
C#
//C# code for the above approachusing System;using System.Collections.Generic;using System.Linq;Â
class GFG{    // Adjacency list to store the edges.    List<List<Tuple<int, int>>> adj = new List<List<Tuple<int, int>>>();Â
    // To store maximum points of a path    // starting at a node    List<int> dp = new List<int>();Â
    // Visited vector to keep track of nodes for    // which dp values have already been calculated    List<bool> vis = new List<bool>();Â
    // To store the final answer    int ans = 0;Â
    // Function for visiting every node and    // calculating dp values for each node.    void dfs(int currNode, List<int> points)    {        // Mark the current node as visited so        // that it does not have to be visited again.        vis[currNode] = true;Â
        // To store maximum path starting        // at node minus length of edge connecting        // that node to the current node for each        // child of the current node.        List<int> childNodes = new List<int>();Â
        // Iterating through each child        // of the current node.        foreach (var edge in adj[currNode])        {            int childNode = edge.Item1;            int edgeLength = edge.Item2;Â
            // To check whether the child has been            // already visited or not            if (!vis[childNode])            {                // Call dfs function for the child                dfs(childNode, points);            }Â
            // Push the value (maximum points path            // starting at this child node minus length            // of edge) into the list            childNodes.Add(dp[childNode] - edgeLength);        }Â
        // Sort the list in decreasing order        // to pick the maximum 2 values.        childNodes.Sort((a, b) => b.CompareTo(a));Â
        // max1 - to store maximum points path        // starting at a child node of the current        // node, max2 - to store the second maximum        // points path starting at a child node        // of the current node.        int max1 = 0, max2 = 0;        if (childNodes.Count >= 2)        {            max1 = Math.Max(max1, childNodes[0]);            max2 = Math.Max(max2, childNodes[1]);        }        else if (childNodes.Count >= 1)        {            max1 = Math.Max(max1, childNodes[0]);        }Â
        // Calculate maximum points path passing        // through the current node.        ans = Math.Max(ans, max1 + max2 + points[currNode]);Â
        // Store the maximum points path starting        // at the current node in dp[currNode]        dp[currNode] = max1 + points[currNode];    }Â
    // To find the maximal points path    int MaxPointPath(int n, List<int> points, List<List<int>> edges)    {        adj = new List<List<Tuple<int, int>>>(n + 1);        dp = new List<int>(n + 1);        vis = new List<bool>(n + 1);Â
        for (int i = 0; i <= n; i++)        {            adj.Add(new List<Tuple<int, int>>());            dp.Add(0);            vis.Add(false);        }Â
        // Filling adjacency list        foreach (var edge in edges)        {            int u = edge[0];            int v = edge[1];            int w = edge[2];            adj[u].Add(new Tuple<int, int>(v, w));            adj[v].Add(new Tuple<int, int>(u, w));        }Â
        // Calling dfs for node 1        dfs(1, points);Â
        return ans;    }Â
    static void Main()    {        GFG obj = new GFG();Â
        // Number of Vertices        int n = 5;Â
        // Points at each node        List<int> points = new List<int>(n + 1)        {            0, 6, 3, 2, 5, 0        };Â
        // Edges and their lengths        List<List<int>> edges = new List<List<int>>        {            new List<int> { 1, 2, 10 },            new List<int> { 2, 3, 3 },            new List<int> { 2, 4, 1 },            new List<int> { 1, 5, 11 }        };Â
        Console.WriteLine(obj.MaxPointPath(n, points, edges));    }} |
Javascript
// Javascript code for the above approachÂ
class GFG {    constructor() {        // Adjacency list to store the edges.        this.adj = [];Â
        // To store maximum points of a path        // starting at a node        this.dp = [];Â
        // Visited vector to keep track of nodes for        // which dp values have already been calculated        this.vis = [];Â
        // To store the final answer        this.ans = 0;    }Â
    // Function for visiting every node and    // calculating dp values for each node.    dfs(currNode, points) {        // Mark the current node as visited so        // that it does not have to be visited again.        this.vis[currNode] = 1;Â
        // To store maximum path starting        // at node minus length of edge connecting        // that node to the current node for each        // child of the current node.        const childNodes = [];Â
        // Iterating through each child        // of the current node.        for (const [child, edgeLength] of this.adj[currNode]) {            // To check whether the child has been            // already visited or not            if (!this.vis[child]) {                // Call dfs function for the child                this.dfs(child, points);            }Â
            // Push the value (maximum points path            // starting at this child node minus length            // of the edge) into the vector            childNodes.push(this.dp[child] - edgeLength);        }Â
        // Sort the vector in decreasing order        // to pick 2 maximum values.        childNodes.sort((a, b) => b - a);Â
        // max1 - to store maximum points path        // starting at the child node of the current        // node, max2 - to store second maximum        // points path starting at the child node        // of the current node.        let max1 = 0,            max2 = 0;        if (childNodes.length >= 2) {            max1 = Math.max(max1, childNodes[0]);            max2 = Math.max(max2, childNodes[1]);        } else if (childNodes.length >= 1) {            max1 = Math.max(max1, childNodes[0]);        }Â
        // Calculate maximum points path passing        // through the current node.        this.ans = Math.max(this.ans, max1 + max2 + points[currNode]);Â
        // Store maximum points path starting        // at the current node in dp[currNode]        this.dp[currNode] = max1 + points[currNode];    }Â
    // To find maximal points path    MaxPointPath(n, points, edges) {        this.adj = Array(n + 1).fill().map(() => []);        this.dp = Array(n + 1).fill(0);        this.vis = Array(n + 1).fill(0);Â
        // Filling adjacency list        for (let i = 0; i < n - 1; i++) {            this.adj[edges[i][0]].push([edges[i][1], edges[i][2]]);            this.adj[edges[i][1]].push([edges[i][0], edges[i][2]]);        }Â
        // Calling dfs for node 1        this.dfs(1, points);Â
        return this.ans;    }}Â
// Driver codeconst obj = new GFG();Â
// Number of Verticesconst n = 5;Â
// Points at each nodeconst points = [0, 6, 3, 2, 5, 0];Â
// Edges and their lengthsconst edges = [    [1, 2, 10],    [2, 3, 3],    [2, 4, 1],    [1, 5, 11]];Â
console.log(obj.MaxPointPath(n, points, edges));Â
// This code is contributed by ragul21 |
7
Time Complexity:Â O(n*logn), since for each node sorting is performed on the values of its children.
Auxiliary Space :Â O(n), where n is the number of nodes in the tree.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!

