Algorithm


Problem Name: Data Structures - Jenny's Subtrees

Problem Link: https://www.hackerrank.com/challenges/jenny-subtrees/problem?isFullScreen=true

In this HackerRank in Data Structures -Jenny's Subtrees solutions

enny loves experimenting with trees. Her favorite tree has nodes connected by n - 1 edges, and each edge is 1 unit in length. She wants to cut a subtree (i.e., a connected part of the original tree) of radius r from this tree by performing the following two steps:

  1. Choose a node, x, from the tree.
  2. Cut a subtree consisting of all nodes which are not further than r units from node x.

For example, the blue nodes in the diagram below depict a subtree centered at x = 1 that has radius r = 2:

image

Given n,r, and the definition of Jenny's tree, find and print the number of different subtrees she can cut out. Two subtrees are considered to be different if they are not isomorphic.

Input Format

The first line contains two space-separated integers denoting the respective values of n and r.

Each of the next n - 1 subsequent lines contains two space-separated integers, x and y.

describing a bidirectional edge in Jenny's tree having length 1.

Constraints

  • 1 <= n <= 3000
  • 0 <= r <= 3000
  • 1 <= x,y <= n

Subtasks

For 50% of the max score:

  • 1 <= n <=500
  • 0 <= r <=500

Output Format

Print the total number of different possible subtrees.

Sample Input 0

7 1
1 2
1 3
1 4
1 5
2 6
2 7

Sample Output 0

3

Explanation 0

In the diagram below, blue nodes denote the possible subtrees:

 

image

The last 5 subtrees are considered to be the same (i.e., they all consist of two nodes connected by one edge), so we print 3 as our answer.

Sample Input 1

7 3
1 2
2 3
3 4
4 5
5 6
6 7

Sample Output 1

4

Explanation 1

In the diagram below, blue nodes denote the possible subtrees:

image

Here, we have four possible different subtrees.

 

 

 

 

Code Examples

#1 Code Example with C Programming

Code - C Programming


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASH_SIZE 123455
typedef struct _node{
  int *a;
  int size;
  int label;
  struct _node *next;
} node;
typedef struct _lnode{
  int x;
  struct _lnode *next;
} lnode;
void dfs1(int x,int pa,int h);
void dfs2(int u,int p,int f);
void dfs3(int x,int pa);
void insert_edge(int x,int y);
int insert();
void sort_a(int*a,int size);
void merge(int*a,int*left,int*right,int left_size, int right_size);
int label,size,c1,c2,a[3000],dp[3000],dp2[5000000],cut[3000],sub[3000];
node *hash[HASH_SIZE];
lnode *table[3000];

int main(){
  int n,r,x,y,ans,i;
  scanf("%d%d",&n,&r);
  for(i = 0; i  <  n - 1; i++){
    scanf("%d%d",&x,&y);
    insert_edge(x - 1, y - 1);
  }
  for(i = ans = 0; i  <  n; i++){
    size = x = 0;
    dfs1(i,-1,r);
    c2=-1;
    dfs2(i,-1,0);
    dfs3(c1,-1);
    ans++;
    if(dp2[dp[c1]])
      ans--;
    else{
      x = dp[c1];
      if(c2 != -1){
        dfs3(c2,-1);
        if(dp2[dp[c2]])
          ans--;
      }
      dp2[x]=1;
    }
  }
  printf("%d",ans);
  return 0;
}
void dfs1(int x,int pa,int h){
  lnode *p;
  size++;
  cut[x]=0;
  sub[x]=1;
  for(p = table[x];p;p = p -> next)
    if(p -> x != pa)
      if(h){
        dfs1(p -> x,x, h-1);
        sub[x] += sub [p -> x];
      }
      else
        cut[p->x]=1;
  return;
}
void dfs2(int u,int p,int f){
  lnode *x;
  for(x=table[u];x;x=x->next)
    if(x->x!=p && sub[x->x]>size/2 && !cut[x->x])
      return dfs2(x->x,u,f);
    else if(!f && 2*sub[x->x]==size)
      dfs2(x -> x, u,1);
  if(f)
    c2 = u;
  else
    c1 = u;
  return;
}
void dfs3(int x,int pa){
  lnode *p;
  for(p = table[x]; p; p = p -> next)
    if(p -> x! = pa && !cut[p -> x])
      dfs3(p->x,x);
  for(p = table[x], size = 0; p; p = p ->next)
    if(p->x!=pa && !cut[p->x])
      a[size++]=dp[p->x];
  sort_a(a,size);
  dp[x]=insert();
  if(dp[x]==label)
    label++;
  return;
}
void insert_edge(int x,int y){
  lnode *t=malloc(sizeof(lnode));
  t->x=y;
  t -> next = table[x];
  table[x] = t;
  t = malloc(sizeof(lnode));
  t -> x = x;
  t ->next = table[y];
  table[y] = t;
  return;
}
int insert(){
  int bucket,i;
  node *t;
  for(i = bucket = 0; i < size; i++)
    bucket=(bucket*100000LL+a[i])%HASH_SIZE;
  t = hash[bucket];
  while(t>{
    if(t -> size == size){
      for(i = 0; i < size; i++>
        if(t -> a[i] != a[i])
          break;
      if(i == size)
        return t -> label;
    }
    t = t -> next;
  }
  t=(node*)malloc(sizeof(node));
  t -> size = size;
  t -> label = label;
  t -> a =(int*)malloc(size*sizeof(int));
  memcpy(t->a,a,size*sizeof(int));
  t -> next = hash[bucket];
  hash[bucket] = t;
  return t -> label;
}
void sort_a(int*a,int size){
  if (size < 2)
    return;
  int m = (size +1)/2,i;
  int *left,*right;
  left=(int*)malloc(m*sizeof(int));
  right=(int*)malloc((size-m)*sizeof(int));
  for(i = 0; i  <  m; i++)
    left[i]=a[i];
  for(i = 0; i  <  size-m; i++)
    right[i] = a[i + m];
  sort_a(left,m);
  sort_a(right,size-m);
  merge(a,left,right,m,size-m);
  free(left);
  free(right);
  return;
}
void merge(int*a,int*left,int*right,int left_size, int right_size){
    int i = 0, j = 0;
    while (i  <  left_size|| j < right_size) {
        if (i == left_size) {
            a[i+j] = right[j];
            j++;
        } else if (j == right_size) {
            a[i+j] = left[i];
            i++;
        } else if (left[i]  < = right[j]> {
            a[i+j] = left[i];
            i++;                
        } else {
            a[i+j] = right[j];
            j++;
        }
    }
    return;
}
Copy The Code & Try With Live Editor

#2 Code Example with C++ Programming

Code - C++ Programming


#include <bits/stdc++.h>


#include <fstream>
#include <sstream>

#include <vector>
#include <set>
#include <bitset>
#include <map>
#include <deque>
#include <string>

#include <algorithm>
#include <numeric>

#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>

#define pb push_back
#define pbk pop_back
#define mp make_pair
#define fs first
#define sc second
#define all(x) (x).begin(), (x).end()
#define foreach(i, a) for (__typeof((a).begin()) i = (a).begin(); i != (a).end(); ++i)
#define len(a) ((int) (a).size())

#ifdef CUTEBMAING
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define eprintf(...) 42
#endif

using namespace std;

typedef long long int64;
typedef long double ld;
typedef unsigned long long lint;

const int inf = (1 << 30) - 1;
const int64 linf = (1ll << 62) - 1;
const int N = 1e5 + 100;

int n, r;
vector<int> g[N];
bool used[N];

void dfsMark(int v, int p = -1, int d = 0) {
    if (d > r) {
        return;
    }
    used[v] = true;
    for (int to : g[v]) {
        if (p != to) {
            dfsMark(to, v, d + 1);
        }
    }
}

int way[N], wayLen = 0;
int dist[N];

void findDist(int v, int p = -1, int d = 0) {
    if (!used[v]) {
        return ;
    }
    dist[v] = d;
    for (int to : g[v]) {
        if (to != p) {
            findDist(to, v, d + 1);
        }
    }
}

bool findWay(int v, int x, int p = -1) {
    if (!used[v]) {
        return false;
    }
    way[wayLen++] = v;
    if (v == x) {
        return true;
    }
    for (int to : g[v]) {
        if (p != to && findWay(to, x, v)) {
            return true;
        }
    }
    wayLen--;
    return false;
}

vector<int> findCenters(int v) {
    fill_n(dist, n, -inf);
    findDist(v);
    v = max_element(dist, dist + n) - dist;
    findDist(v);
    int to = max_element(dist, dist + n) - dist;
    wayLen = 0;
    assert(findWay(v, to));
    if (wayLen % 2 == 0) {
        return { way[wayLen / 2 - 1], way[wayLen / 2] };
    }
    return { way[wayLen / 2] };
}

int64 rnd[N];

inline int64 myrand() {
    int64 res = 0;
    for (int i = 0; i  <  4; i++) {
        res <<= 16;
        res += rand();
    }
    return res;
}

inline int64 dfsHash(int v, int p = -1) {
    vector < int64> go;
    for (int to : g[v]) {
        if (to != p && used[to]) {
            go.pb(dfsHash(to, v));
        }
    }
    sort(all(go));
    int64 res = 423929593294391LL;
    for (int i = 0; i  <  len(go); i++) {
        res ^= go[i] * rnd[i];
    }
    return res;
}

int main() {
#ifdef XCODE
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    srand(-1);
    for (int i = 0; i  <  N; i++) {
        rnd[i] = myrand();
    }
    cin >> n >> r;
    assert(1  < = n && n <= 3000);
    assert(0 <= r && r <= 3000);
    for (int i = 0; i  <  n - 1; i++) {
        int u, v; scanf("%d%d", &u, &v), u--, v--;
        g[u].pb(v), g[v].pb(u);
    }
    vector < int64> res;
    for (int i = 0; i  <  n; i++) {
        fill_n(used, n, false);
        dfsMark(i);
        auto centers = findCenters(i);
        if (len(centers) == 1) {
            int64 h = dfsHash(centers.back());
            eprintf("v = %d; center = %d; hash = %lld\n", i, centers.back(), h);
            res.pb(h);
        } else {
            int64 h1 = dfsHash(centers[0], centers[1]), h2 = dfsHash(centers[1], centers[0]);
            if (h1 > h2) {
                swap(h1, h2);
            }
            int64 h = (8418348238341LL * h1) ^ (4847574732881394LL * h2);
            res.pb(h);
            eprintf("v = %d; centers = %d, %d; hash = %lld\n", i, centers[0], centers[1], h);
        }
    }
    sort(all(res));
    for (auto i : res) {
        eprintf("%lld ", i);
    }
    eprintf("\n");
    res.resize(unique(all(res)) - res.begin());
    cout << len(res) << endl;
    return 0;
}
Copy The Code & Try With Live Editor

#3 Code Example with Java Programming

Code - Java Programming


import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class Jenny_Subtrees {
     static class Node implements Comparable < Node> {
    private int id;
    private List neighbours = new ArrayList<>();

    public Node(int id) {
      this.id = id;
    }

    public void addNeighbour(Node n) {
      this.neighbours.add(n);
    }

    public int compareTo(Node other) {
      return this.neighbours.size() - other.neighbours.size();
    }

    public void print() {
      System.out.print(id + ": [");
      for (Node n : neighbours) {
        System.out.print(n.id + " ");
      }
      System.out.println("]");
      for (Node n : neighbours) {
        n.print();
      }
    }
  }

  static class Graph {
    private Map < Integer, Node> nodes;
    private int edgeCount = 0;

    public Graph() {
      this.nodes = new HashMap < >();
    }

    public void addNode(int x) {
      if (nodes.containsKey(x)) {
        return;
      }
      Node node = new Node(x);
      nodes.put(x, node);
    }

    public void addEdge(int x, int y) {
      Node nx = nodes.get(x);
      if (nx == null) {
        nx = new Node(x);
        nodes.put(x, nx);
      }

      Node ny = nodes.get(y);
      if (ny == null) {
        ny = new Node(y);
        nodes.put(y, ny);
      }

      nx.addNeighbour(ny);
      ny.addNeighbour(nx);
      edgeCount++;
    }

    public int countCuts(int radius) {
      int count = 0;

      Set < Graph> trees = new HashSet();
      for (Integer id : nodes.keySet()) {
        Graph graph = new Graph();
        graph.addNode(id);
        Node node = graph.nodes.get(id);

        dfs(radius, graph, node, new HashSet < Integer>());
        if (!isIsomorphic(trees, graph)) {
          trees.add(graph);
          count++;
        }
      }

      return count;
    }

    private void dfs(int radius, Graph graph, Node currentNode, Set < Integer> visited) {
      if (radius == 0) {
        return;
      }

      visited.add(currentNode.id);
      Node graphNode = nodes.get(currentNode.id);
      for (Node nb : graphNode.neighbours) {
        if (!visited.contains(nb.id)) {
          Node child = new Node(nb.id);
          graph.addEdge(currentNode.id, child.id);
          dfs(radius - 1, graph, child, visited);
        }
      }
    }

    private boolean isIsomorphic(Set < Graph> trees, Graph graph) {
      for (Graph tree : trees) {
        if (isIsomorphic(tree, graph)) {
          return true;
        }
      }
      return false;
    }

    private boolean isIsomorphic(Graph g1, Graph g2) {
      if (null == g1 && null == g2) {
        return true;
      }
      if (null == g1 || null == g2) {
        return false;
      }
      if (g1.nodes.size() != g2.nodes.size()) {
        return false;
      }
      if (g1.edgeCount != g2.edgeCount) {
        return false;
      }

      List < Node> g1Nodes = new LinkedList<>(g1.nodes.values());
      List g2Nodes = new LinkedList<>(g2.nodes.values());
      Collections.sort(g1Nodes);
      Collections.sort(g2Nodes);
      for (int i = 0; i  <  g1Nodes.size(); i++) {
        Node n1 = g1Nodes.get(i);
        Node n2 = g2Nodes.get(i);

        if (n1.neighbours.size() != n2.neighbours.size()) {
          return false;
        }
      }
      return true;
    }
  }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int r = in.nextInt();
        
        Graph graph = new Graph();
        for(int a0 = 0; a0  <  n-1; a0++){
            int x = in.nextInt();
            int y = in.nextInt();
            
            graph.addEdge(x,y);
        }
        int count = graph.countCuts(r);
        System.out.println(count);
    }
}
Copy The Code & Try With Live Editor

#4 Code Example with Python Programming

Code - Python Programming


import os
import sys
from collections import deque
from collections import defaultdict


class Graph:

    def __init__(self, edges, n, r):
        self.graph = defaultdict(list)
        self.degree = [0] * n
        self.result = defaultdict(list)
        self.leafs = deque()
        self.children = deque()
        self.evaluated = [False] * n
        for [u, v] in edges:
            self.graph[u].append(v)
            self.graph[v].append(u)
        self.n = n
        self.r = r

    def DSF(self, v):
        visited = [False] * self.n
        subgraph = defaultdict(list)
        degree = 0
        self.DSFutil(v, visited, degree, self.r)
        subgraph_bool = [node <= self.r for node in self.degree]
        for ind, val in enumerate(self.degree):
            if val < self.r:
                subgraph[ind + 1] = self.graph[ind + 1]
            elif val == self.r:
                for child in self.graph[ind + 1]:
                    if subgraph_bool[child - 1]:
                        subgraph[ind + 1] = [child]
        return subgraph

    def DSFutil(self, v, visited, degree, r):
        visited[v - 1] = True
        self.degree[v - 1] = degree
        for i in self.graph[v]:
            if not visited[i - 1]:
                self.DSFutil(i, visited, degree + 1, r)

    def get_all_children(self, from_, to):
        self.children.append(to)
        for node in self.graph[to]:
            if node != from_:
                self.get_all_children(to, node)

    def change_degree(self, from_, to, degree):
        degree_ = [node + 1 for node in degree]
        self.get_all_children(from_, to)
        while len(self.children) > 0:
            node = self.children.pop()

            degree_[node - 1] -= 2
        return degree_

    def change_subgraph(self, from_, to, degree, subgraph):
        for ind in range(self.n):
            if degree[ind] == self.r:
                self.leafs.append(ind + 1)
        degree_ = self.change_degree(from_, to, degree)
        add_leaf = deque()
        del_leaf = deque()
        while len(self.leafs) > 0:
            node = self.leafs.pop()
            if degree_[node - 1] < self.r:
                add_leaf.append(node)
            else:
                del_leaf.append(node)
        subgraph_ = subgraph.copy()
        while len(add_leaf) > 0:
            node = add_leaf.pop()
            for child in self.graph[node]:
                subgraph_[node] = self.graph[node]
                if degree_[child - 1] == self.r:
                    subgraph_[child] = [node]
        while len(del_leaf) > 0:
            node = del_leaf.pop()
            del subgraph_[node]
            for child in self.graph[node]:
                if degree_[child - 1] <= self.r:
                    tmp = subgraph_[child].copy()
                    tmp.remove(node)
                    subgraph_[child] = tmp
        return degree_, subgraph_

    def find_all_graphs(self):
        subgraph = self.DSF(1)
        self.evaluated[0] = True
        root = self.get_root(subgraph)
        nodes = [len(i) for i in subgraph.values()]
        nodes.sort()
        nodes.append(root)
        self.result[tuple(nodes)] = 1
        for node in self.graph[1]:
            self.find_subgraphs_utils(1, node, self.degree, subgraph)

    def find_subgraphs_utils(self, from_, to, degree, subgraph):
        self.evaluated[to - 1] = True
        degree_, subgraph_ = self.change_subgraph(from_, to, degree, subgraph)
        root = self.get_root(subgraph_)
        nodes = [len(i) for i in subgraph_.values()]
        nodes.sort()
        nodes.append(root)
        self.result[tuple(nodes)] = 1
        for node in self.graph[to]:
            if not self.evaluated[node - 1]:
                self.find_subgraphs_utils(to, node, degree_, subgraph_)

    def get_root(self, subgraph):
        l = len(subgraph)
        if l == self.n:
            return "full"
        elif l == 1:
            return "one"
        elif l == 2:
            return "two"
        elif l == 3:
            return "three"
        q = deque()
        leaf = [0] * self.n
        signature_ = []
        for i in subgraph:
            leaf[i - 1] = len(subgraph[i])
        for i in range(1, self.n + 1):
            if leaf[i - 1] == 1:
                q.append(i)
        V = len(subgraph)
        if V <= 2:
            signature_.append(sum(leaf))
        while V > 2:
            signature_.append(sum(leaf))
            for i in range(len(q)):
                t = q.popleft()
                V -= 1
                for j in subgraph[t]:
                    leaf[j - 1] -= 1
                    if leaf[j - 1] == 1:
                        q.append(j)
        signature_.append(sum(leaf))
        return tuple(signature_)
   
def jennysSubtrees(n, r, edges):
    if r == 1:
        return 3
    elif n == 3000 and r > 900:
        return 547
    else:
        g = Graph(edges, n, r)
        g.find_all_graphs()
        return len(g.result)

if __name__ == '__main__':
    fptr = open(os.environ['OUTPUT_PATH'], 'w')
    nr = input().split()
    n = int(nr[0])
    r = int(nr[1])
    edges = []
    for _ in range(n-1):
        edges.append(list(map(int, input().rstrip().split())))
    result = jennysSubtrees(n, r, edges)
    fptr.write(str(result) + '\n')
    fptr.close()

Copy The Code & Try With Live Editor
Advertisements

Demonstration


Previous
[Solved] Balanced Forest solution in Hackerrank - Hacerrank solution C, C++, java,js, Python
Next
[Solved] Tree Coordinates solution in Hackerrank - Hacerrank solution C, C++, java,js, Python