Open In App

Maximal Point Path

Last Updated : 16 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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:

Treedrawio-(1)

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:

Treedrawio-(2)

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 code
int 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 Implementation
class 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 code
if __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 approach
using 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 code
const obj = new GFG();
 
// Number of Vertices
const n = 5;
 
// Points at each node
const points = [0, 6, 3, 2, 5, 0];
 
// Edges and their lengths
const 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


Output

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.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads