Number of subsets with product less than k using DP (original) (raw)
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.
Table of Content
- Using Recursion - O(2^n) Time and O(n) Space
- Using Top - Down Dp (memoization) - O(n*k) Time and O(n*k) Space
- Using Dynamic Programming (Tabulation) - O(n*k) Time and O(n*k) Space
- Using Space Optimised DP - O(n*k) Time and O(k) Space
**Using Recursion - O(2^n) Time and O(n) Space
We can identify a recursive pattern in this problem. There are two state variables:
- The **current index i in the array arr[].
- 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).
I**nclude 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.
C++ `
// A C++ program for Number of subsets // with product less than k using recursion #include <bits/stdc++.h> using namespace std;
// Recursive function to count subsets whose product // is less than or equal to k int countSubsets(int i, int currentProduct, int k, vector &arr) {
// Get the size of the array
int n = arr.size();
// Base case: if we have considered all elements
// Return 1 if the current product is less than or equal to k,
// otherwise return 0
if (i == n)
return (currentProduct <= k);
// Case 1: Exclude the current element and move to the next
int exclude = countSubsets(i + 1, currentProduct, k, arr);
// Case 2: Include the current element in the subset
int include = 0;
// Only include the current element if the product remains <= k
if ((arr[i] * currentProduct) <= k)
include = countSubsets(i + 1, currentProduct * arr[i], k, arr);
// Return the total count of subsets including both cases
return (include + exclude);}
int numOfSubsets(vector& arr, int k) {
// Call the recursive function starting from index 0
// Initial product of 1
// Subtract 1 from the result to exclude the empty subset
return countSubsets(0, 1, k, arr) - 1;}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 10;
cout << numOfSubsets(arr, k);
return 0;}
Java
// A Java program for Number of subsets // with product less than k using recursion import java.util.*;
class GfG {
// Recursive function to count subsets whose product is
// less than or equal to k
static int countSubsets(int i, int currentProduct, int k, int[] arr) {
int n = arr.length;
// Base case: if we have considered all elements
if (i == n)
return (currentProduct <= k) ? 1 : 0;
// Case 1: Exclude the current element
int exclude = countSubsets(i + 1, currentProduct, k, arr);
// Case 2: Include the current element
int include = 0;
if ((arr[i] * currentProduct) <= k)
include = countSubsets(i + 1, currentProduct * arr[i], k, arr);
return include + exclude;
}
static int numOfSubsets(int[] arr, int k) {
// Subtract 1 to exclude the empty subset
return countSubsets(0, 1, k, arr) - 1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
int k = 10;
System.out.println(numOfSubsets(arr, k));
}}
Python
A Python program for Number of subsets
with product less than k using recursion
def countSubsets(i, currentProduct, k, arr): n = len(arr)
# Base case: if we have considered all elements
if i == n:
return 1 if currentProduct <= k else 0
# Case 1: Exclude the current element
exclude = countSubsets(i + 1, currentProduct, k, arr)
# Case 2: Include the current element
include = 0
if currentProduct * arr[i] <= k:
include = countSubsets(i + 1, currentProduct * arr[i], k, arr)
return include + excludedef numOfSubsets(arr, k):
# Subtract 1 to exclude the empty subset
return countSubsets(0, 1, k, arr) - 1if name == "main": arr = [1, 2, 3, 4] k = 10 print(numOfSubsets(arr, k))
C#
// A C# program for Number of subsets // with product less than k using recursion using System;
class GfG {
// Recursive function to count subsets whose
// product is less than or equal to k
static int CountSubsets(int i, int currentProduct, int k, int[] arr){
int n = arr.Length;
// Base case: if we have considered all elements
if (i == n)
return currentProduct <= k ? 1 : 0;
// Case 1: Exclude the current element
int exclude = CountSubsets(i + 1, currentProduct, k, arr);
// Case 2: Include the current element
int include = 0;
if (currentProduct * arr[i] <= k)
include = CountSubsets(i + 1, currentProduct * arr[i], k, arr);
return include + exclude;
}
static int numOfSubsets(int[] arr, int k){
// Subtract 1 to exclude the empty subset
return CountSubsets(0, 1, k, arr) - 1;
}
static void Main(string[] args){
int[] arr = { 1, 2, 3, 4 };
int k = 10;
Console.WriteLine(numOfSubsets(arr, k));
}}
JavaScript
// A Javascript program for Number of subsets // with product less than k using recursion
function countSubsets(i, currentProduct, k, arr) { let n = arr.length;
// Base case: if we have considered all elements
if (i === n) {
return currentProduct <= k ? 1 : 0;
}
// Case 1: Exclude the current element
let exclude = countSubsets(i + 1, currentProduct, k, arr);
// Case 2: Include the current element
let include = 0;
if (currentProduct * arr[i] <= k) {
include = countSubsets(i + 1, currentProduct * arr[i], k, arr);
}
return include + exclude;}
function numOfSubsets(arr, k) {
// Subtract 1 to exclude the empty subset
return countSubsets(0, 1, k, arr) - 1;}
let arr = [1, 2, 3, 4]; let k = 10; console.log(numOfSubsets(arr, k));
`
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. C++ `
// A C++ program to count the number of subsets // with a product less than or equal to k memoization #include <bits/stdc++.h> using namespace std;
// Recursive function to count subsets whose product is // less than or equal to k with memoization int countSubsets(int i, int currentProduct, int k, vector &arr, vector<vector> &memo) {
int n = arr.size();
// Base case: if we have considered all elements
if (i == n) {
// Return 1 if the current product is less than or
// equal to k, otherwise 0
return (currentProduct <= k);
}
// Check if the current state is already computed
if (memo[i][currentProduct] != -1)
return memo[i][currentProduct];
// Case 1: Exclude the current element and move to the next
int exclude = countSubsets(i + 1, currentProduct, k, arr, memo);
// Case 2: Include the current element in the subset
int include = 0;
if ((arr[i] * currentProduct) <= k)
include = countSubsets(i + 1, currentProduct * arr[i], k, arr, memo);
// Store the result in the memo table and return the total count
return memo[i][currentProduct] = (include + exclude);}
// Wrapper function to calculate the number of subsets // whose product is less than or equal to k int numOfSubsets(vector arr, int k) { int n = arr.size();
// Memoization table to store intermediate results
// Initialized with -1, indicating uncomputed states
vector<vector<int>> memo(n + 1, vector<int>(k + 1, -1));
// Call the recursive function starting from index 0
// Initial product of 1
// Subtract 1 to exclude the empty subset
return countSubsets(0, 1, k, arr, memo) - 1;}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 10;
cout << numOfSubsets(arr, k);
return 0;}
Java
// A Java program to count the number of subsets // with a product less than or equal to k memoization import java.util.Arrays;
class GfG { static int countSubsets(int i, int currentProduct, int k, int[] arr, int[][] memo) { int n = arr.length;
// Base case: if all elements are considered
if (i == n) {
return currentProduct <= k ? 1 : 0;
}
// Check if result is already computed
if (memo[i][currentProduct] != -1) {
return memo[i][currentProduct];
}
// Exclude current element
int exclude = countSubsets(i + 1, currentProduct, k, arr, memo);
// Include current element if valid
int include = 0;
if (currentProduct * arr[i] <= k) {
include = countSubsets(i + 1, currentProduct * arr[i], k, arr, memo);
}
// Store the result and return
memo[i][currentProduct] = exclude + include;
return memo[i][currentProduct];
}
static int numOfSubsets(int[] arr, int k) {
int n = arr.length;
int[][] memo = new int[n + 1][k + 1];
// Initialize memoization table
for (int[] row : memo) {
Arrays.fill(row, -1);
}
// Compute result, subtracting 1 to
// exclude the empty subset
return countSubsets(0, 1, k, arr, memo) - 1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
int k = 10;
System.out.println(numOfSubsets(arr, k));
}}
Python
A Python program to count the number of subsets
with a product less than or equal to k memoization
def countSubsets(i, currentProduct, k, arr, memo): n = len(arr)
# Base case: if all elements are considered
if i == n:
return 1 if currentProduct <= k else 0
# Check if result is already computed
if memo[i][currentProduct] != -1:
return memo[i][currentProduct]
# Exclude current element
exclude = countSubsets(i + 1, currentProduct, k, arr, memo)
# Include current element if valid
include = 0
if currentProduct * arr[i] <= k:
include = countSubsets(i + 1, currentProduct * arr[i], k, arr, memo)
# Store the result and return
memo[i][currentProduct] = exclude + include
return memo[i][currentProduct]def numOfSubsets(arr, k): n = len(arr)
# Initialize memoization table
memo = [[-1] * (k + 1) for _ in range(n + 1)]
# Compute result, subtracting 1 to exclude
# the empty subset
return countSubsets(0, 1, k, arr, memo) - 1arr = [1, 2, 3, 4] k = 10 print(numOfSubsets(arr, k))
C#
// A C# program to count the number of subsets // with a product less than or equal to k memoization using System;
class GfG { static int CountSubsets(int i, int currentProduct, int k, int[] arr, int[,] memo) { int n = arr.Length;
// Base case: if all elements are considered
if (i == n) {
return currentProduct <= k ? 1 : 0;
}
// Check if result is already computed
if (memo[i, currentProduct] != -1) {
return memo[i, currentProduct];
}
// Exclude current element
int exclude = CountSubsets(i + 1, currentProduct, k, arr, memo);
// Include current element if valid
int include = 0;
if (currentProduct * arr[i] <= k) {
include = CountSubsets(i + 1, currentProduct * arr[i], k, arr, memo);
}
// Store the result and return
memo[i, currentProduct] = exclude + include;
return memo[i, currentProduct];
}
static int numOfSubsets(int[] arr, int k) {
int n = arr.Length;
// Initialize memoization table
int[,] memo = new int[n + 1, k + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) {
memo[i, j] = -1;
}
}
// Compute result, subtracting 1 to exclude the empty subset
return CountSubsets(0, 1, k, arr, memo) - 1;
}
static void Main(string[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 10;
Console.WriteLine(numOfSubsets(arr, k));
}}
JavaScript
// A Javascript program to count the number of subsets // with a product less than or equal to k memoization
function countSubsets(i, currentProduct, k, arr, memo) { const n = arr.length;
// Base case: if all elements are considered
if (i === n) {
return currentProduct <= k ? 1 : 0;
}
// Check if result is already computed
if (memo[i][currentProduct] !== -1) {
return memo[i][currentProduct];
}
// Exclude current element
let exclude = countSubsets(i + 1, currentProduct, k, arr, memo);
// Include current element if valid
let include = 0;
if (currentProduct * arr[i] <= k) {
include = countSubsets(i + 1, currentProduct * arr[i], k, arr, memo);
}
// Store the result and return
memo[i][currentProduct] = exclude + include;
return memo[i][currentProduct];}
function numOfSubsets(arr, k) { const n = arr.length; const memo = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1));
// Compute result, subtracting 1 to exclude the empty subset
return countSubsets(0, 1, k, arr, memo) - 1;}
const arr = [1, 2, 3, 4]; const k = 10; console.log(numOfSubsets(arr, k));
`
**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 productvalue 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 productvalue as less than or equal to k will be **dp[n][k].
C++ `
// A C++ program to count the number of subsets // with a product less than or equal to k using tabulation #include <bits/stdc++.h> using namespace std;
int numOfSubsets(vector &arr, int k) {
int n = arr.size();
// Initialize all values of dp[i][j] to 1 to
// include the empty subset.
vector<vector<int>> dp(n + 1, vector<int>(k + 1, 1));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// Case 1: Exclude the current element.
dp[i][j] = dp[i - 1][j];
// Case 2: Include the current element.
// if arr[i-1] is less than equal to j we include it.
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][j / arr[i - 1]];
}
}
}
// Return the total count of subsets with product ≤ k, subtracting 1
// to exclude the empty subset from the result.
return dp[n][k] - 1;}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 10;
cout << numOfSubsets(arr, k);
return 0;}
Java
// A Java program to count the number of subsets // with a product less than or equal to k using tabulation import java.util.*;
class GfG {
static int numOfSubsets(int[] arr, int k) { int n = arr.length;
// Initialize the DP table with 1s to
// include the empty subset.
int[][] dp = new int[n + 1][k + 1];
for (int i = 0; i <= n; i++) {
Arrays.fill(dp[i], 1);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// Case 1: Exclude the current element
dp[i][j] = dp[i - 1][j];
// Case 2: Include the current element
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][j / arr[i - 1]];
}
}
}
// Return the total count of subsets
// with product ≤ k, subtracting 1
return dp[n][k] - 1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
int k = 10;
System.out.println(numOfSubsets(arr, k));
}}
Python
A Python program to count the number of subsets
with a product less than or equal to k using tabulation
def numOfSubsets(arr, k): n = len(arr)
# Initialize the DP table with 1s to
# include the empty subset.
dp = [[1] * (k + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, k + 1):
# Case 1: Exclude the current element
dp[i][j] = dp[i - 1][j]
# Case 2: Include the current element
if j >= arr[i - 1]:
dp[i][j] += dp[i - 1][j // arr[i - 1]]
# Return the total count of subsets
# with product ≤ k, subtracting 1
return dp[n][k] - 1arr = [1, 2, 3, 4] k = 10 print(numOfSubsets(arr, k))
C#
// A C# program to count the number of subsets // with a product less than or equal to k using tabulation using System;
class GfG { static int numOfSubsets(int[] arr, int k) {
int n = arr.Length;
// Initialize the DP table with 1s to
// include the empty subset.
int[,] dp = new int[n + 1, k + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++) {
dp[i, j] = 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
// Case 1: Exclude the current element
dp[i, j] = dp[i - 1, j];
// Case 2: Include the current element
if (j >= arr[i - 1]) {
dp[i, j] += dp[i - 1, j / arr[i - 1]];
}
}
}
// Return the total count of subsets with
// product ≤ k, subtracting 1
return dp[n, k] - 1;
}
static void Main(string[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 10;
Console.WriteLine(numOfSubsets(arr, k));
}}
JavaScript
// A Javascript program to count the number of subsets // with a product less than or equal to k using tabulation
function numOfSubsets(arr, k) { let n = arr.length;
// Initialize the DP table with 1s to include the empty subset.
let dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(1));
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= k; j++) {
// Case 1: Exclude the current element
dp[i][j] = dp[i - 1][j];
// Case 2: Include the current element
if (j >= arr[i - 1]) {
dp[i][j] += dp[i - 1][Math.floor(j / arr[i - 1])];
}
}
}
// Return the total count of subsets
// with product ≤ k, subtracting 1
return dp[n][k] - 1;}
let arr = [1, 2, 3, 4]; let k = 10; console.log(numOfSubsets(arr, k));
`
**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;
C++ `
// A C++ program to count the number of subsets // with a product less than or equal to k // using space-optimized tabulation.
#include <bits/stdc++.h> using namespace std;
// Function to count subsets with product less than or equal to k int numOfSubsets(vector &arr, int k) {
int n = arr.size();
// Initialize two arrays to store the previous and current state
// Both initialized with 1 to include the empty subset
vector<int> prevState(k + 1, 1), curState(k + 1, 1);
for (int i = 1; i <= n; i++) {
// Copy the previous state to the current state
curState = prevState;
for (int j = 1; j <= k; j++) {
// If the current element can be included in subsets
// Add the count of subsets ending at the previous element
// where the product multiplied by the current element is ≤ k
if (j >= arr[i - 1]) {
curState[j] += prevState[j / arr[i - 1]];
}
}
// Update the previous state for the next iteration
prevState = curState;
}
// Return the total count of subsets with product ≤ k, subtracting 1
// to exclude the empty subset
return curState[k] - 1;}
int main() {
vector<int> arr = {1, 2, 3, 4};
int k = 10;
cout << numOfSubsets(arr, k);
return 0;}
Java
// A Java program to count the number of subsets // with a product less than or equal to k // using space-optimized tabulation.
import java.util.Arrays;
class GfG {
static int numOfSubsets(int[] arr, int k) {
int n = arr.length;
// Initialize two arrays for previous and current states
int[] prevState = new int[k + 1];
int[] curState = new int[k + 1];
Arrays.fill(prevState, 1);
Arrays.fill(curState, 1);
for (int i = 1; i <= n; i++) {
// Copy previous state to current state
curState = Arrays.copyOf(prevState, k + 1);
for (int j = 1; j <= k; j++) {
// Include current element if valid
if (j >= arr[i - 1]) {
curState[j] += prevState[j / arr[i - 1]];
}
}
// Update previous state
prevState = curState;
}
// Subtract 1 to exclude the empty subset
return curState[k] - 1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
int k = 10;
System.out.println(numOfSubsets(arr, k));
}}
Python
A Python program to count the number of subsets
with a product less than or equal to k
using space-optimized tabulation.
def numOfSubsets(arr, k): n = len(arr)
# Initialize previous and current state arrays
prevState = [1] * (k + 1)
curState = [1] * (k + 1)
for i in range(1, n + 1):
# Copy previous state to current state
curState = prevState[:]
for j in range(1, k + 1):
# Include current element if valid
if j >= arr[i - 1]:
curState[j] += prevState[j // arr[i - 1]]
# Update previous state
prevState = curState
# Subtract 1 to exclude the empty subset
return curState[k] - 1arr = [1, 2, 3, 4] k = 10 print(numOfSubsets(arr, k))
C#
// A C# program to count the number of subsets // with a product less than or equal to k // using space-optimized tabulation. using System;
class GfG {
static int numOfSubsets(int[] arr, int k) { int n = arr.Length;
// Initialize previous and current
// state arrays
int[] prevState = new int[k + 1];
int[] curState = new int[k + 1];
Array.Fill(prevState, 1);
Array.Fill(curState, 0);
for (int i = 0; i < n; i++) {
// Copy previous state to current
// state for this iteration
Array.Copy(prevState, curState, k + 1);
for (int j = 1; j <= k; j++) {
// Include the current element if
// it can be part of a subset
if (j >= arr[i]) {
curState[j] += prevState[j / arr[i]];
}
}
// Update previous state for the
// next iteration
Array.Copy(curState, prevState, k + 1);
}
// Subtract 1 to exclude the empty
// subset from the result
return curState[k] - 1;
}
static void Main(string[] args) {
int[] arr = { 1, 2, 3, 4 };
int k = 10;
Console.WriteLine(numOfSubsets(arr, k));
}}
JavaScript
// A Javascript program to count the number of subsets // with a product less than or equal to k // using space-optimized tabulation.
function numOfSubsets(arr, k) { let n = arr.length;
// Initialize previous and current state arrays
let prevState = Array(k + 1).fill(1);
let curState = Array(k + 1).fill(1);
for (let i = 1; i <= n; i++) {
// Copy previous state to current state
curState = [...prevState];
for (let j = 1; j <= k; j++) {
// Include current element if valid
if (j >= arr[i - 1]) {
curState[j] += prevState[Math.floor(j / arr[i - 1])];
}
}
// Update previous state
prevState = curState;
}
// Subtract 1 to exclude the empty subset
return curState[k] - 1;}
let arr = [1, 2, 3, 4]; let k = 10; console.log(numOfSubsets(arr, k));
`
**Related article: