Dynamic Programming Help: Binary Tree Cost Edge - algorithm

Given a binary tree with n leaves and a set of C colors. Each leaf node of the tree is given a unique color from the set C. Thus no leaf nodes have the same color. The internal nodes of the tree are uncolored. Every pair of colors in the set C has a cost associated with it. So if a tree edge connects two nodes of colors A and B, the edge cost is the cost of the pair (A, B). Our aim is to give colors to the internal nodes of the tree, minimizing the total edge cost of the tree.
I have working on this problem for hours now, and haven't really come up with a working solution. Any hints would be appreciated.

I am going to solve the problem with pseudocode, because I tried writing explanation and it was completely not understandable even for me. Hopefully the code will do the trick. The complexity of my solution is not very good - memory an druntime in O(C^2 * N).
I will need couple of arrays I will be using in dynamic approach to your task:
dp [N][C][C] -> dp[i][j][k] the maximum price you can get from a tree rooted at node i, if you paint it in color j and its parent is colored in color k
maxPrice[N][C] -> maxPrice[i][j] the maximum price you can get from a tree rooted in node i if its parent is colored in color j
color[leaf] -> the color of the leaf leaf
price[C][C] -> price[i][j] the price you get if you have pair of neighbouring nodes with colors i and j
chosenColor[N][C] -> chosenColor[i][j] the color one should choose for node i to obtain maxPrice[i][j]
Lets assume the nodes are ordered using topological sorting, i.e we will be processing first leaves. Topological sorting is very easy to do in a tree. Let the sorting have given a list of inner nodes inner_nodes
for leaf in leaves:
for i in 0..MAX_C, j in 0..MAX_C
dp[leaf][i][j] = (i != color[leaf]) ? 0 : price[i][j]
for i in 0..MAX_C,
maxPrice[leaf][i] = price[color[leaf]][i]
chosenColor[leaf][i] = color[leaf]
for node in inner_nodes
for i in 0..MAX_C, j in 0..MAX_C
dp[node][i][j] = (i != root) ? price[i][j] : 0
for descendant in node.descendants
dp[node][i][j] += maxPrice[descendant][i]
for i in 0...MAX_C
for j in 0...MAX_C
if maxPrice[node][i] < dp[node][j][i]
maxPrice[node][i] = dp[node][j][i]
chosenColor[node][i] = j
for node in inner_nodes (reversed)
color[node] = (node == root) ? chosenColor[node][0] : chosenColor[node][color[parent[node]]

As a starting point, you can use a greedy solution which gives you an upper bound on the total cost:
while the root is not colored
pick an uncolored node having colored descendants only
choose the color that minimizes the total cost to its descendants

Related

Calculate the number of nodes on either side of an edge in a tree

A tree here means an acyclic undirected graph with n nodes and n-1 edges. For each edge in the tree, calculate the number of nodes on either side of it. If on removing the edge, you get two trees having a and b number of nodes, then I want to find those values a and b for all edges in the tree (ideally in O(n) time).
Intuitively I feel a multisource BFS starting from all the "leaf" nodes would yield an answer, but I'm not able to translate it into code.
For extra credit, provide an algorithm that works in any general graph.
Run a depth-first search (or a breadth-first search if you like it more) from any node.
That node will be called the root node, and all edges will be traversed only in the direction from the root node.
For each node, we calculate the number of nodes in its rooted subtree.
When a node is visited for the first time, we set this number to 1.
When the subtree of a child is fully visited, we add the size of its subtree to the parent.
After this, we know the number of nodes on one side of each edge.
The number on the other side is just the total minus the number we found.
(The extra credit version of your question involves finding bridges in the graph on top of this as a non-trivial part, and thus deserves to be asked as a separate question if you are really interested.)
Consider the following tree:
1
/ \
2 3
/ \ | \
5 6 7 8
If we cut the edge between node 1 and 2, The tree will surely split into two tree because there is only one unique edge between two nodes according to tree property:
1
\
3
| \
7 8
and
2
/ \
5 6
So, now a is the number of nodes rooted at 1 and b is number of nodes rooted at 2.
> Run one DFS considering any node as root.
> During DFS, for each node x, calculate nodes[x] and parent[x] where
nodes [x] = k means number of nodes of sub-tree rooted at x is k
parent[x] = y means y is parent of x.
> For any edge between node x and y where parent[x] = y:
a := nodes[root] - nodes[x]
b := nodes[x]
Time and space complexity both O(n).
Note that n=b-a+1. Due to this, you don't need to count both sides of the edge. This greatly simplifies things. A normal recursion over the nodes starting from the root is enough. Since your tree is undirected you don't really have a "root", just pick one of the leaves.
What you want to do is to "go down" the tree until you reach the bottom. Then you count backwards from there. The leaf returns 1, and each recursive step sums the return values for each edge and then increment by 1.
Here is the Java code. Function countEdges() takes in the adjacency list of the tree as an argument also current node and the parent node of the current node(here parent node means that current node was introduced by parent node in this DFS).
Here edge[][] stores the number of nodes on one side of the edge[i][j], obviously the number of nodes on the other side will be equal to (total nodes - edge[i][j]).
int edge[][];
int countEdges(ArrayList<Integer> adj[], int cur, int par) {
// If current nodes is leaf node and is not the node provided by the calling function then return 1
if(adj[cur].size() == 1 && par != 0) return 1;
int count = 1;
// count the number of nodes recursively for each neighbor of current node.
for(int neighbor: adj[cur]) {
if(neighbor == par) continue;
count += countEdges(adj, neighbor, cur);
}
// while returning from recursion assign the result obtained in the edge[][] matrix.
return edge[par][cur] = count;
}
Since we are visiting each node only once in the DFS time complexity should be O(V).

Algorithms-how to find a subset in weighted binary tree that maximises weight and no pair of nodes have parent-child relationship

So i am on my algorithms and complexity homework and there is an exercise that's a pain. i have a binary tree with weight labels(integers) on each node and an integer k and i have to find a subset of the nodes containing at most k nodes , that maximizes the sum of weights and no pair of nodes in the subset have parent-child relationship . I am supposed to utilize dynamic programming to solve that .
So as a first notion i was thinking of checking the sum of all the subsets with cardinality 2 ( that have the required property ) , and scale to cardinality k using the subproblems to construct the solution . i have problems all the way though . Any thoughts ?
Here is a dynamic programming solution:
The state is (node, set_size, is_taken). The value is the maximum sum of a set such that all nodes in this set belong to a subtree rooted in a node, its size is set_size and is_taken indicates whether the node itself belongs to this set or not.
The base case is as follows: for all leaves, f(leaf, 0, false) = 0(it means that this leaf is not taken) and f(leaf, 1, true) = the weight of this leaf (it means that this leaf is taken).
We can compute the values of f for inner nodes in the following manner: first, we compute it for a node's children, and then we merge the results(either taking this node or not).
The answer is the maximum of max(f(root, k', false), f(root, k', true)) among all k' <= k.
This problem is similar to the well-known maximum independent set problem.
Hint: You will have to construct a 2-D matrix where
DP[i][j] = Maximum node weight till node i taking at-most j nodes.
DP[i][j] ~ Max(DP[parent(i)][j], DP[grandparent(i)][j-1] + Wi)
Fill up the matrix by conducting a DFS on the tree starting from the root.
Note:
The above recurrence relation is just an idea to get you started. The exact relation shall have to be worked out taking into account the DFS and order of traversal of nodes.
You can define it as
T(v, parent_chosen)
T is the maximum subset sum for the subtree rooted at v whether v's parent was chosen or not.
T(null, _) = 0
#if v's parent was chosen only check for children
T(v, true) = T(left(v), false)+T(right(v), false)
#if v's parent wasn't chosen, try both options (choosing and not choosing)
T(v, false) = max(
T(left(v), true)+T(right(v), true)+weight(v),
T(left(v), false)+T(right(v), false)
)
The solution is given by:
T(root, false)
This solution is O(n).

the center of a tree with weighted edges

i am trying to give a solution to my university assignement..given a connected tree T=(V,E). every edge e has a specific positive cost c..d(v,w) is the distance between node v and w..I'm asked to give the pseudocode of an algorithm that finds the center of such a tree(the node that minimizes the maximum distance to every other node)..
My solution consists first of all in finding the first two taller branches of the tree..then the center will be in the taller branch in a distance of H/2 from the root(H is the difference between the heights of the two taller branches)..the pseudocode is:
Algorithm solution(Node root, int height, List path)
root: the root of the tree
height : the height measured for every branch. Initially height=0
path : the path from the root to a leaf. Initially path={root}
Result : the center of the tree
if root==null than
return "error message"
endif
/*a list that will contain an element <h,path> for every
leaf of the tree. h is the distanze of the leaf from the root
and path is the path*/
List L = empty
if isLeaf(root) than
L = L union {<height,path>}
endif
foreach child c of root do
solution(c,height+d(root,c),path UNION {c})
endfor
/*for every leaf in the tree I have stored in L an element containing
the distance from the root and the relative path. Now I'm going to pick
the two most taller branches of the tree*/
Array array = sort(L)
<h1,path1> = array[0]//corresponding to the tallest branch
<h2,path2> = array[1]//corresponding to the next tallest branch
H = h1 - h2;
/*The center will be the node c in path1 with d(root,c)=H/2. If such a
node does not exist we can choose the node with te distance from the root
closer to H/2 */
int accumulator = 0
for each element a in path1 do
if d(root,a)>H/2 than
return MIN([d(root,a)-H/2],[H/2-d(root,a.parent)])
endif
end for
end Algorithm
is this a correct solution??is there an alternative and more efficient one??
Thank you...
Your idea is correct. You can arbitrarily pick any vertex to be a root of the tree, and then traverse the tree in 'post-order'. Since the weights are always positive, you can always pick the two longest 'branches' and update the answer in O(1) for each node.
Just remember that you are looking for the 'global' longest path (i.e. diameter of the graph), rather than the 'local' longest paths that go through the roots of the subtrees.
You can find more information if you search for "(weighted) Jordan Center (in a tree)". The optimal algorithm is O(N) for trees, so asymptotically your solution is optimal since you only use a single DFS which is O(|V| + |E|) == O(|V|) for a tree.

Find max subset of tree with max distance not greater than K

I run into a dynamic programming problem on interviewstreet named "Far Vertices".
The problem is like:
You are given a tree that has N vertices and N-1 edges. Your task is
to mark as small number of verices as possible so that the maximum
distance between two unmarked vertices be less than or equal to K. You
should write this value to the output. Distance between two vertices i
and j is defined as the minimum number of edges you have to pass in
order to reach vertex i from vertex j.
I was trying to do dfs from every node of the tree, in order to find the max connected subset of the nodes, so that every pair of subset did not have distance more than K.
But I could not define the state, and transitions between states.
Is there anybody that could help me?
Thanks.
The problem consists essentially of finding the largest subtree of diameter <= k, and subtracting its size from n. You can solve it using DP.
Some useful observations:
The diameter of a tree rooted at node v (T(v)) is:
1 if n has no children,
max(diameter T(c), height T(c) + 1) if there is one child c,
max(max(diameter T(c)) for all children c of v, max(height T(c1) + height T(c2) + 2) for all children c1, c2 of v, c1 != c2)
Since we care about maximizing tree size and bounding tree diameter, we can flip the above around to suggest limits on each subtree:
For any tree rooted at v, the subtree of interest is at most k deep.
If n is a node in T(v) and has no children <= k away from v, its maximum size is 1.
If n has one child c, the maximum size of T(n) of diameter <= k is max size T(c) + 1.
Now for the tricky bit. If n has more than one child, we have to find all the possible tree sizes resulting from allocating the available depth to each child. So say we are at depth 3, k = 7, we have 4 depth left to play with. If we have three children, we could allocate all 4 to child 1, 3 to child 1 and 1 to child 2, 2 to child 1 and 1 to children 2 and 3, etc. We have to do this carefully, making sure we don't exceed diameter k. You can do this with a local DP.
What we want for each node is to calculate maxSize(d), which gives the max size of the tree rooted at that node that is up to d deep that has diameter <= k. Nodes with 0 and 1 children are easy to figure this for, as above (for example, for one child, v.maxSize(i) = c.maxSize(i - 1) + 1, v.maxSize(0) = 1). Nodes with 2 or more children, you compute dp[i][j], which gives the max size of a k-diameter-bound tree using up to the ith child taking up to j depth. The recursion is dp[i][j] = max(child(i).maxSize(m - 1) + dp[i - 1][min(j, k - m)] for m from 1 to j. d[i][0] = 1. This says, try giving the ith child 1 to j depth, and give the rest of the available depth to the previous nodes. The "rest of the available depth" is the minimum of j, the depth we are working with, or k - m, because depth given to child i + depth given to the rest cannot exceed k. Transfer the values of the last row of dp to the maxSize table for this node. If you run the above using a depth-limited DFS, it will compute all the necessary maxSize entries in the correct order, and the answer for node v is v.maxSize(k). Then you do this once for every node in the tree, and the answer is the maximum value found.
Sorry for the muddled nature of the explanation. It was hard for me to think through, and difficult to describe. Working through a few simple examples should make it clearer. I haven't calculated the complexity, but n is small, and it went through all the test cases in .5 to 1s in Scala.
A few basic things I can notice (maybe very obvious to others):
1. There is only one route possible between two given vertices.
2. The farthest vertices would be the one with only one outgoing edge.
Now to solve the issue.
I would start with the set of Vertices that have only one edge and call them EDGE[] calculate the distances between the vertices in EDGE[]. This will give you (EDGE[i],EDGE[j],distance ) value pairs
For all the vertices pairs in EDGE that have a distance of > K, DO EDGE[i].occur++,EDGE[i].distance = MAX(EDGE[i].distance, distance)
EDGE[j].occur++,EDGE[j].distance = MAX(EDGE[j].distance, distance)
Find the CANDIDATES in EDGE[] that have max(distance) from those Mark the with with max (occur)
Repeat till all edge vertices pair have distance less then or equal to K

Finding the heaviest length-constrained path in a weighted Binary Tree

UPDATE
I worked out an algorithm that I think runs in O(n*k) running time. Below is the pseudo-code:
routine heaviestKPath( T, k )
// create 2D matrix with n rows and k columns with each element = -∞
// we make it size k+1 because the 0th column must be all 0s for a later
// function to work properly and simplicity in our algorithm
matrix = new array[ T.getVertexCount() ][ k + 1 ] (-∞);
// set all elements in the first column of this matrix = 0
matrix[ n ][ 0 ] = 0;
// fill our matrix by traversing the tree
traverseToFillMatrix( T.root, k );
// consider a path that would arc over a node
globalMaxWeight = -∞;
findArcs( T.root, k );
return globalMaxWeight
end routine
// node = the current node; k = the path length; node.lc = node’s left child;
// node.rc = node’s right child; node.idx = node’s index (row) in the matrix;
// node.lc.wt/node.rc.wt = weight of the edge to left/right child;
routine traverseToFillMatrix( node, k )
if (node == null) return;
traverseToFillMatrix(node.lc, k ); // recurse left
traverseToFillMatrix(node.rc, k ); // recurse right
// in the case that a left/right child doesn’t exist, or both,
// let’s assume the code is smart enough to handle these cases
matrix[ node.idx ][ 1 ] = max( node.lc.wt, node.rc.wt );
for i = 2 to k {
// max returns the heavier of the 2 paths
matrix[node.idx][i] = max( matrix[node.lc.idx][i-1] + node.lc.wt,
matrix[node.rc.idx][i-1] + node.rc.wt);
}
end routine
// node = the current node, k = the path length
routine findArcs( node, k )
if (node == null) return;
nodeMax = matrix[node.idx][k];
longPath = path[node.idx][k];
i = 1;
j = k-1;
while ( i+j == k AND i < k ) {
left = node.lc.wt + matrix[node.lc.idx][i-1];
right = node.rc.wt + matrix[node.rc.idx][j-1];
if ( left + right > nodeMax ) {
nodeMax = left + right;
}
i++; j--;
}
// if this node’s max weight is larger than the global max weight, update
if ( globalMaxWeight < nodeMax ) {
globalMaxWeight = nodeMax;
}
findArcs( node.lc, k ); // recurse left
findArcs( node.rc, k ); // recurse right
end routine
Let me know what you think. Feedback is welcome.
I think have come up with two naive algorithms that find the heaviest length-constrained path in a weighted Binary Tree. Firstly, the description of the algorithm is as follows: given an n-vertex Binary Tree with weighted edges and some value k, find the heaviest path of length k.
For both algorithms, I'll need a reference to all vertices so I'll just do a simple traversal of the Tree to have a reference to all vertices, with each vertex having a reference to its left, right, and parent nodes in the tree.
Algorithm 1
For this algorithm, I'm basically planning on running DFS from each node in the Tree, with consideration to the fixed path length. In addition, since the path I'm looking for has the potential of going from left subtree to root to right subtree, I will have to consider 3 choices at each node. But this will result in a O(n*3^k) algorithm and I don't like that.
Algorithm 2
I'm essentially thinking about using a modified version of Dijkstra's Algorithm in order to consider a fixed path length. Since I'm looking for heaviest and Dijkstra's Algorithm finds the lightest, I'm planning on negating all edge weights before starting the traversal. Actually... this doesn't make sense since I'd have to run Dijkstra's on each node and that doesn't seem very efficient much better than the above algorithm.
So I guess my main questions are several. Firstly, do the algorithms I've described above solve the problem at hand? I'm not totally certain the Dijkstra's version will work as Dijkstra's is meant for positive edge values.
Now, I am sure there exist more clever/efficient algorithms for this... what is a better algorithm? I've read about "Using spine decompositions to efficiently solve the length-constrained heaviest path problem for trees" but that is really complicated and I don't understand it at all. Are there other algorithms that tackle this problem, maybe not as efficiently as spine decomposition but easier to understand?
You could use a DFS downwards from each node that stops after k edges to search for paths, but notice that this will do 2^k work at each node for a total of O(n*2^k) work, since the number of paths doubles at each level you go down from the starting node.
As DasBoot says in a comment, there is no advantage to using Dijkstra's algorithm here since it's cleverness amounts to choosing the shortest (or longest) way to get between 2 points when multiple routes are possible. With a tree there is always exactly 1 way.
I have a dynamic programming algorithm in mind that will require O(nk) time. Here are some hints:
If you choose some leaf vertex to be the root r and direct all other vertices downwards, away from the root, notice that every path in this directed tree has a highest node -- that is, a unique node that is nearest to r.
You can calculate the heaviest length-k path overall by going through each node v and calculating the heaviest length-k path whose highest node is v, finally taking the maximum over all nodes.
A length-k path whose highest node is v must have a length-i path descending towards one child and a length-(k-i) path descending towards the other.
That should be enough to get you thinking in the right direction; let me know if you need further help.
Here's my solution. Feedback is welcome.
Lets treat the binary tree as a directed graph, with edges going from parent to children. Lets define two concepts for each vertex v:
a) an arc: which is a directed path, that is, it starts from vertex v, and all vertices in the path are children of the starting vertex v.
b) a child-path: which is a directed or non-directed path containing v, that is, it could start anywhere, end anywhere, and go from child of v to v, and then, say to its other child. The set of arcs is a subset of the set of child-paths.
We also define a function HeaviestArc(v,j), which gives, for a vertex j, the heaviest arc, on the left or right side, of length j, starting at v. We also define LeftHeaviest(v,j), and RightHeaviest(v,j) as the heaviest left and right arcs of length j respectively.
Given this, we can define the following recurrences for each vertex v, based on its children:
LeftHeaviest(v,j) = weight(LeftEdge(v)) + HeaviestArc(LeftChild(v)),j-1);
RightHeaviest(v,j) = weight(RightEdge(v)) + HeaviestArc(RightChild(v)),j-1);
HeaviestArc(v,j) = max(LeftHeaviest(v,j),RightHeaviest(v,j));
Here j here goes from 1 to k, and HeaviestArc(v,0)=LeftHeaviest(v,0),RightHeaviest(v,0)=0 for all. For leaf nodes, HeaviestArc(v,0) = 0, and HeaviestArc(v,j)=-inf for all other j (I need to think about corner cases more thoroughly).
And then HeaviestChildPath(v), the heaviest child-path containing v, can be calculated as:
HeaviestChildPath(v) = max{ for j = 0 to k LeftHeaviest(j) + RightHeaviest(k-j)}
The heaviest path should be the heaviest of all child paths.
The estimated runtime of the algorithm should be order O(kn).
def traverse(node, running_weight, level):
if level == 0:
if max_weight < running_weight:
max_weight = running_weight
return
traverse(node->left,running_weight+node.weight,level-1)
traverse(node->right,running_weight+node.weight,level-1)
traverse(node->parent,running_weight+node.weight,level-1)
max_weight = 0
for node in tree:
traverse(node,0,N)

Resources