Algorithm
Problem Name: Data Structures -
https://www.hackerrank.com/challenges/jenny-subtrees/problem?isFullScreen=true
In this HackerRank in Data Structures -
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:
- Choose a node, x, from the tree.
- 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:
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:
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:
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