VOOZH about

URL: https://www.geeksforgeeks.org/dsa/count-subsequences-product-less-k/

⇱ Number of subsets with product less than k using DP - GeeksforGeeks


  • Courses
  • Tutorials
  • Interview Prep

Number of subsets with product less than k using DP

Last Updated : 9 Dec, 2024

Given an array arr[] of n elements. The task is to find the number of non-empty subsets whose product of elements is less than or equal to a given integer k.

Examples:

Input: arr[] = [2, 4, 5, 3], k = 12
Output: 8
Explanation: All possible subsets whose products are less than 12 are: (2), (4), (5), (3), (2, 4), (2, 5), (2, 3), (4, 3)

Input: arr[] = [9, 8, 3], k = 2
Output: 0
Explanation: There are no subsets with products less than or equal to 2.

Using Recursion - O(2^n) Time and O(n) Space

We can identify a recursive pattern in this problem. There are two state variables:

  1. The current index i in the array arr[].
  2. The cumulative product currentProduct of the subsets being considered.

We consider two cases for recursion:

Exclude the current element: The current element is not included in the subset, and the product remains unchanged. This corresponds to:

  • numOfSubsets(i + 1, currentProduct, k).

Include the current element: The current element is included in the subset, and the product is updated as currentProduct * arr[i]. This corresponds to:

  • numOfSubsets(i + 1, currentProduct * arr[i], k)

Recurrence relation:

  • numOfSubsets(i, currentProduct, k) = numOfSubsets(i+1, currentProduct, k) + numOfSubsets(i+1, currentProduct*arr[i], k)​

Base Case: If i == n (all elements have been processed), return 1 if the currentProduct less than equal to k else 0.


Output
11

Using Top - Down Dp (memoization) - O(n*k) Time and O(n*k) Space

If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming:

1.Optimal Substructure: Maximum subsequence length for a given i, j and currentProduct , i.e. numOfSubsets(i + 1, currentProduct, k), depends on the optimal solutions of the subproblems numOfSubsets(i + 1, currentProduct , k) and numOfSubsets(i + 1, currentProduct * arr[i], k). By choosing the total of these optimal substructures, we can efficiently calculate answer.

2. Overlapping Subproblems: While applying a recursive approach in this problem, we notice that certain subproblems are computed multiple times. For example while considering arr = [2, 3, 6, 8] and k = 10, countSubsets(3, 6, 10, arr) computed multiple times from countSubsets(2, 1, 10, arr) and countSubsets(2, 6, 10, arr).

  • There are two parameter that change in the recursive solution: i going from 0 to n-1, currentProduct going from 1 to k. So we create a 2D array of size (n+1)*(k+1) for memoization.
  • We initialize this array as -1 to indicate nothing is computed initially.
  • Now we modify our recursive solution to first check if the value is -1, then only make recursive calls. This way, we avoid re-computations of the same subproblems.

Output
11

Using Dynamic Programming (Tabulation) - O(n*k) Time and O(n*k) Space

We create a 2D array dp[n+1][k+1], such that dp[i][j] equals to the number of subsets having product value less than equal to j from subsets of arr[0...i-1].

We fill the dp array as following: 

  • We initialize all values of dp[i][j] as 1 because we take empty subsequence as our base case.

Iterate over all the values of arr[i] from left to right and for each arr[i], iterate over all the possible values of k i.e. from 1 to k (both inclusive) and fill the dp array as following: 

dp[i][j] = dp[i­-1][j]

if j>=arr[i-1]
dp[i][j] +=dp[i­-1][j/arr[i-1]] 

This can be explained as there are only two cases either we take element arr[i] or we don't. We take a element only when it's value is less than or equal to j. Then we look for subsets ending at i-1 such that their product with arr[i] should be atmost k. Product of those subsets will be less than or equal to a j/arr[i-1] so we get this value from dp[i-1][j/arr[i-1]] The number of subsets from set arr[0..n] having product value as less than or equal to k will be dp[n][k].


Output
11

Using Space Optimised DP - O(n*k) Time and O(k) Space

In previous approach the current value dp[i][j] is only depend upon the current and previous row values of DP. So to optimize the space complexity we use a two 1D array of size k+1 namely prevState and curState to store the computations. The final answer is equal to curState[k]-1;


Output
11

Related article:

Comment
Article Tags: