1

Problem Statement:
You are given an integer N denoting number of nodes in that tree. Now, you need to count, how many distinct paths are there in the tree, such that, minimum node value in that path is greater than or equal to k.

Input format:

  1. First line contains total number of nodes N in that tree and a positive integer value K.
  2. Next N-1 lines contain a pair of integers u, v (values are not comma seperated), which denote that there is an edge between node u and v, in the tree.

Example:

Input:

4 2  
1 2  
2 3  
3 4  

Expected Output:

3

Edit: I am not able to think of how to approach this problem. So, please provide me with a hint, so that i can further try implementing it. I'll be greatful to even the slightest help.

Update:

1 <= N <= 10^5
1 <= u,v <= N  
1 <= K <= 10^6  

A naive approach to such a problem will not pass under any cirumstances. The complexity of the solution should be either O(n**2) or O(nlogn).

Deadpool
  • 2,198
  • 9
  • 22
  • 1
    @גלעדברקן I just want hint to determine a way, to obtain all possible paths from root node to the child node, that satisfy the given constraint in least complexity. If there is an algorithm for the same, then i can easily determine the total number of subpaths using `nC2`, where `n` is the count of total number of nodes in that distinct path. – Deadpool Feb 10 '19 at 12:19
  • Where in the question does it say "root node to child node?" It just says, "distinct paths ... in the tree..." – גלעד ברקן Feb 10 '19 at 14:21
  • Yes, you're right. But in a tree, you have a hierarichal relationship among the nodes. So, i used the term `root node to child node`. – Deadpool Feb 10 '19 at 14:22
  • Consider a tree having 20 nodes. One of the path from starting node to end node has 5 nodes in total, and all of them satisfy the given constraint. Hence, it can be easily said that the total number of distinct paths in that route will be `5C2`. – Deadpool Feb 10 '19 at 14:25
  • 1
    Please use a term other than "root" to describe a starting point for a path. It's too confusing :) (I couldn't understand your previous comment.) (And to the moderators out there - please stop moving comments to chat when the comments are relevant to the question proper!) – גלעד ברקן Feb 10 '19 at 14:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188179/discussion-between-taurus05-and--). – Deadpool Feb 10 '19 at 14:32
  • Please clarify if this problem is about a tree or a general graph. – גלעד ברקן Feb 11 '19 at 11:56
  • @גלעדברקן It's about the tree because, if it was a graph, then suppose if we want to determine the distinct paths from that node `x` to the rest of the nodes and, if node `x` was a part of the cycle, then the path length would be infinite if it would have satisfied all the constraints. Hence, it is definitely a tree. Under no circumstances, it will be a graph. – Deadpool Feb 11 '19 at 12:29
  • Technically, not all graphs contain cycles. But even with a directed acyclic graph, I think my answer should work. – גלעד ברקן Feb 11 '19 at 23:49
  • @גלעדברקן, The data structure involved in this problem is not a graph (neither cyclic nor acyclic). And as far as your solution is concerned, the data structure you have used is really complex and the code is not at all readable due to the usage of irrelevant variable names like `f` and `a, b& c`. – Deadpool Feb 12 '19 at 04:32
  • Not only are the variables I used relevant, but the answer runs with O(n) complexity. Feel free to ask if something seems confusing. – גלעד ברקן Feb 12 '19 at 10:57

3 Answers3

2

This problem can be solved using Dynamic Programming on Trees, you can read about it here https://www.geeksforgeeks.org/dynamic-programming-trees-set-2/.

Let's split the problem into two parts, the first one is to find the number of paths that are valid in the subtree of a node u. The second part is to find the answer for the node u if we do not consider its subtree but going to it's parent and so on.

Let us consider 1 as the root of the tree.

Now for solving the first part, we will make an array in[] in which we will store the the number of paths in the subtree of node u so in[u] will represent the number of valid paths starting from the node u and visiting its subtree. To compute this array we can run a simple dfs as follows :

//u is the current node - p is the parent
void dfs1(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { //iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { //in case v is the parent just ignore it
            dfs1(v, u); //go to node v
            if (u >= k && v >= k) { //if value of u or v is less than k then we cant start at this node so it should be 0
                in[u] += in[v] + 1; //add to number of paths of u the number of paths starting from the node v + 1 (+1 because we can also go from u to v)
            }
        }
    }
}

For solving the second part we need an array out[] with out[u] being the number of paths that are valid if we do not consider the subtree of u which is going to the parent of u and so on.

lets call the parent of u P[u]. To compute out[u] we will rely on p[u]. out[i] is number of valid paths if we go to p[u], and what we can do once we reach p[u] is either go through its subtree (excluding the brach of u of course) or visit p[p[u]] (the parent of parent of u) so out[u] is (out[p[u]] + 1) + (in[p[u]] - in[u] - 1).

// u is the current node - p is the parent
void dfs2(int u, int p) {
    for (int i = 0; i < g[u].size(); i++) { // iterate through the adjacency of node u
        int v = g[u][i]; // v is the child of u
        if (v != p) { // in case v is the parent just ignore it
            if (v >= k && u >= k) { // if value of u or v is less than k then we cant start at this node so it should be 0
                out[v] += (out[u] + 1); //add to the number of paths of v the number of paths from it's parent (u) + 1 if we dont use the subtree of u
                out[v] += (in[u] - in[v] - 1); // add to the number of paths of v the number of paths from it's parent (u)
                // we should subtract in[v] + 1 because we want to exclude this branch from the subtree of the parent
            }
            dfs2(v, u); // go to node v
        }
    }
}

To find the answer just sum all out[u] + in[u] for all the nodes and divide by 2 because each path was computed twice.

complexity O(V+E)

Walid
  • 167
  • 1
  • 1
  • 6
1

Let's solve this problem in a more simple case, assume that all nodes in the tree is greater than k, so the number of valid path is nC2.

And, we also make an observation that, a valid path could not contain any node that is less than k, so, we will need to remove all nodes that less than k from the tree, which will create n - k subtree, thus the final result will be

result = sum of nC2 of all subtree

Simple algorithm:

remove all edges that connect to nodes that less than k

for each node that >= k and not marked as visited
  do bfs to count number of node in that subtree
  result += nC2

return result
Pham Trung
  • 10,784
  • 2
  • 20
  • 41
  • I guess, this is the most apt solution till now.. I'm waiting for more expert reviews to this approach, because i am not able to decide whether it is the best approach or somewhat better approach still exists. – Deadpool Feb 10 '19 at 14:31
  • "assume that all nodes in the tree is greater than k, so the number of valid path is nC2" -- what about input `k=1, 1 -> 2, 2 -> 3, 1 -> 4, 4 -> 5` 5 choose 2 = 10, but we have only 3 + 3 distinct valid paths. Did I misunderstand your answer? (I added a recursive answer, by the way.) – גלעד ברקן Feb 12 '19 at 12:35
  • @גלעדברקן Hmm, I think there are 10 paths: `1 -> 2, 1 -> 3, 1 -> 4, 1 -> 5, 2->3, 2->4, 2->5, 3->4, 3->5, 4->5`. Am I missing something? – Pham Trung Feb 12 '19 at 13:43
  • Yes, you are missing something. In the example in my comment above, 5 or 4 are not accessible from 2. – גלעד ברקן Feb 12 '19 at 13:51
  • @גלעדברקן in that case, you are missing the requirement that this is a tree :), if this is a directed graph, so you may be right? But I think this is not directed, as there is no concept of directed tree, and the problem statement is `an edge between node u and v`, not `an edge from u to v`, direction is not implied here. – Pham Trung Feb 12 '19 at 14:09
  • In my example, 2 and 4 are the children of 1. This is a tree. Why would the right subtree be accessible from the left, as you seem to be suggesting? Maybe I was wrong to assume that paths cannot go from one subtree to the other? – גלעד ברקן Feb 12 '19 at 14:13
  • Here's a quote from the OPs first comment under the question: "..want hint to determine a way, to obtain all possible paths from root node to the child node." (OP means root for any subtree.) – גלעד ברקן Feb 12 '19 at 14:23
  • The question isn't whether there are directed trees but whether the paths are assumed directed top to bottom. The OP (and I) seemed to think so but maybe the OP is mistaken. – גלעד ברקן Feb 12 '19 at 14:36
  • The word, "between," does not imply direction, as you've indicated, but the choice of ordering for any pair of nodes in the input could imply direction. – גלעד ברקן Feb 12 '19 at 14:45
  • @גלעדברקן Hmm, you have a point, If that the case, and if this is a valid tree, so I think the algo will need to modify a little bit :) – Pham Trung Feb 12 '19 at 15:45
  • This way it's good. All possibilities are covered between the answers :) – גלעד ברקן Feb 12 '19 at 15:56
1

For trees, assuming the paths we are enumerating are directed from top to bottom, we can formulate it recursively. Let f(T, k) represent a tuple, [a, b], where a is the number of distinct valid paths in T that start at T; and b, the number of distinct valid paths in T that start at a lower node. All nodes in valid paths have a value greater than or equal to k.

Then (Python code):

def f(T, k):
  if not T["children"]:
    return [0, 0]

  result = [0, 0]

  for c in T["children"]:
    [a, b] = f(c, k)
    result[1] += a + b

    if T["value"] >= k <= c["value"]:
      # One valid path from T to c
      # plus extending all the paths
      # that start at c
      result[0] += 1 + a

  return result

The answer would then be a + b, after calling f from the tree root.

Output:

T = {
  "value": 1,
  "children": [
    {
      "value": 2,
      "children": [
        {
          "value": 3,
          "children": [
            {
              "value": 4,
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

print f(T, 2)  # [0, 3]
גלעד ברקן
  • 21,095
  • 3
  • 19
  • 57