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:
.png)
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:
.png)
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
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.