Given a sorted array keys[0.. n-1] of search keys and an array freq[0.. n-1] of frequency counts, where freq[i] is the number of searches for keys[i]. Construct a binary search tree of all keys such that the total cost of all the searches is as small as possible.
The cost of a BST node is the level of that node multiplied by its frequency. The level of the root is 1.
Examples:
Input: keys[] = [10, 12], freq[]= [34, 50] Output: 118 Explanation: There can be following two possible BSTs The cost of tree I is 34*1 + 50*2 = 134 The cost of tree II is 50*1 + 34*2 = 118
Input: keys[] = [10, 12, 20], freq[]= [34, 8, 50] Output: 142 Explanation: There can be many possible BSTs. Among all possible BSTs, cost of the fifth BST is 1*50 + 2*34 + 3*8 = 142 which is the minimum.
We would multiply each frequency by its tree level, but keeping track of levels makes the solution complicated.
Instead of that, we use a smarter approach. Whenever we choose a node as the root, we add the sum of the frequencies of all the nodes between the start and end index (i to j). After choosing the root, every node in that group (both left and right subtree) automatically shifts one level deeper. When recursion goes deeper and solves the smaller subtrees, their frequency sum is added again automatically, which has the exact same effect as multiplying each node by its level.
[Naive Approach] Using Recursion - O(2^n) Time and O(n) Space
As discussed in our intuition, we try each key as the root of the tree. For every root choice, we recursively compute the cost of its left and right subtrees. At each step, we add the sum of freq in that range to account for all keys moving one level deeper. We repeat this process for all possible roots and finally pick the configuration that gives the minimum total cost.
Output
142
[Expected Approach 1] Using Top - Down Dp (Memoization) - O(n^3) Time and O(n^2) Space
If we look at the recursive approach, we notice that many subproblems are solved repeatedly, which leads to exponential time complexity. To avoid this, we use memoization.
We create a 2D DP array of size n x n to store the results of already solved subproblems. Whenever we encounter a subproblem, If the answer is already computed, we simply return it instead of recalculating.
Output
142
[Expected Approach 2] Using Bottom - Up Dp (Tabulation) - O(n^3) Time and O(n^2) Space
In the tabulation approach, we solve the problem step by step instead of recursion.
We make a table dp[i][j] where each cell stores the minimum cost of a BST for keys from index i to j. The base case is simple: if there is only one key, the cost is just its frequency. Then, we fill the table for bigger ranges of keys. For each range, we try every key as the root, calculate the cost of the left and right parts using the table, and add the sum of frequencies for that range. We keep the minimum cost in the table. At the end, dp[0][n-1] gives the minimum cost of the BST for all keys.