Last Updated: 12 Jan, 2021

Goku and Dragon Balls

Moderate
Asked in companies
OlaAtlassianAmazon

Problem statement

Goku has ‘N’ Dragon Balls. Each Dragon Ball is unique with the ith Dragon Ball having ‘i’ stars on it. For example, the first Dragon Ball has 1 star, the second Dragon Ball has 2 stars, and so on.

Goku gave a task to Gohan to arrange the ‘N’ Dragon Balls in a binary tree-like structure. While making the binary tree, he has to make sure that these conditions must be fulfilled:

The left subtree of any particular Dragon Ball ‘D’ will always contain Dragon Balls with the number of stars less than that of the Dragon Ball ‘D’.

The right subtree of any particular Dragon Ball ‘D’ will always contain Dragon Ball's with the number of stars greater than that of the Dragon Ball ‘D’.

Can you find out how many structurally unique binary trees Gohan can make by fulfilling these conditions?

Note:
The number of structurally unique binary trees can be very large, so return the number of structurally unique binary trees modulo 10^9 + 7.
Input Format
The first line of input contains an integer 'T' which denotes the number of test cases or queries to be run. Then the test cases follow.

The first and the only line of each test case contains a single integer ‘N’ denoting the number of Dragon Balls.
Output Format :
Print the number of structurally unique binary trees satisfying the given conditions Gohan can make.

Print the output of each test case in a separate line.
Note:
You do not need to print anything; it has already been taken care of. Just implement the given function.
Constraints:
1 <= T <= 100
1 <= N <= 5000

Time Limit: 1 second

Approaches

01 Approach

From the given conditions it is clear that we have to find how many structurally unique binary search trees are possible.

 

To find the unique BSTs,  we define two functions:

 

A(i,N): It denotes the number of unique BSTs, with ‘i’ (1 <= ‘i’ <= N) as the root.

B(N): It denotes the number of unique BSTs for a sequence of length N (number of Dragon Balls).

 

Now, we construct B(N) by the sum of A(i, N) :

B(N) = A(1, N) + A(2, N) + … + A(N, N)

 

Notice that when we select ‘i’ as a root i.e. A(i, N), we have i - 1 nodes which can be used to form a left subtree and n - 1 nodes to form a right subtree.

A(i, N) = B(i - 1) * B(N - i)

 

Thus, A(i, N) can be calculated by the product of the number of unique BSTs with i - 1 nodes and the number of unique BSTs with N - i nodes. Uniqueness is guaranteed by the sizes of the left subtree and the right subtree.

 

Here is the algorithm :

 

  • If N is 0 or 1 then we return 1 because the number of unique BSTs with 0 nodes or 1 node is 1.
  • We initialize ‘sum’ to 0. ‘sum’ stores gokuAndDragonBalls(N).
    • We iterate form i = 1 to i = N and call the recursive function.
    • We update sum as sum += gokuAndDragonBalls(i - 1) * gokuAndDragonBalls(n - i).
  • Finally, we return the sum as our answer.

02 Approach

If we draw the recursion tree for the recurrence relation of approach 1, we can observe that there are a lot of overlapping subproblems. 

 

              

Since the problem has overlapping subproblems, we can solve it more efficiently using memorization. 

 

We take an extra array ‘memo’ of size N to store previously calculated results. We use here the same recurrence relation of the previous approach with the ‘memo’ array.

 

Algorithm:

 

The algorithm is similar to the previous approach with some additions. 

We initialize a memo[n+1] array with -1. memo[i] will store the number of different unique BSTs having the number of Dragon balls as ‘i’.

Before calling the function for any valid gokuAndDragonBalls(i), we will check whether we already have a solution corresponding to the gokuAndDragonBalls(i) present in memo

 

  • If we already have a solution corresponding to gokuAndDragonBalls(i), we return that solution i.e memo[i].
  • Else we initialise ‘sum’ to 0 to store gokuAndDragonBalls(i).
  • We iterate from i = 1 to i = N and call the recursive function.
    • sum += gokuAndDragonBalls(i - 1) * gokuAndDragonBalls(n - i)
  • We then set memo[i] to sum and return memo[i] as our answer.

03 Approach

Initially, we were breaking a large problem into smaller subproblems but now, let us look at it differently. The idea is to solve the small problem first and then reach the final answer. Thus we will be using a bottom-up approach now. 

 

Algorithm:

 

Let’s define a function gokuAndDragonBalls(N), where N is the number of Dragon Balls

  • We initialize a dp[N+1] array with 0. dp[i] will store the number of different unique BSTs having  ‘i’ number of Dragon balls.
  • We initialise dp[0] = 1 and dp[1] = 1 as the number of unique BSTs with 0 nodes or 1 node is 1 (base case).
  • Now, we iterate form i = 2 to i = N.
    • sum = 0
    • Iterate ‘k’ from 1 to i:
      • sum = sum + dp[ k - 1 ] * dp[ i - k ]
      • dp[i] = sum
  • Finally, we return dp[n] as answer.