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 meet in middle approach - O(n*2n/2) time and O(2n/2) space
First of all, we simply divide the given array into two equal parts and after that, we generate all possible subsets for both parts of the array and store the value of the elements product for each subset separately into two containers (say subset1 & subset2). Then we combine the results of two halves to give a final answer. This technique is generally referred to as meet in the middle.
Algorithm:
- Divide array into two equal parts.
- Generate all subsets and for each subset calculate product of elements and store this to a container. Try this for both part of array.
- Sort both container which contains products of elements for each possible subsets.
- Traverse any one container and find upper-bound of element to find how many subsets are there whose product of elements is less than k.
Some key points to improve complexity:
- Ignore elements from array if greater than k.
- Ignore product of elements to push into container(subset1 or subset2) if greater than k.
// A C++ program to find the count subset having product
// less than k
#include <bits/stdc++.h>
using namespace std;
int numOfSubsets(vector<int> &arr, int k) {
int n = arr.size();
// declare four vector for dividing array into
// two halves and storing product value of
// possible subsets for them
vector<int> vect1, vect2, subset1, subset2;
// ignore element greater than k and divide
// array into 2 halves
for (int i = 0; i < n; i++) {
// ignore element if greater than k
if (arr[i] > k)
continue;
if (i <= n / 2)
vect1.push_back(arr[i]);
else
vect2.push_back(arr[i]);
}
// generate all subsets for 1st half (vect1)
for (int i = 0; i < (1 << vect1.size()); i++) {
int value = 1;
for (int j = 0; j < vect1.size() and value <= k; j++) {
if (i & (1 << j))
value *= vect1[j];
}
// push only in case subset product is less
// than equal to k
if (value <= k)
subset1.push_back(value);
}
// generate all subsets for 2nd half (vect2)
for (int i = 0; i < (1 << vect2.size()); i++) {
int value = 1;
for (int j = 0; j < vect2.size() and value <= k; j++) {
if (i & (1 << j))
value *= vect2[j];
}
// push only in case subset product is
// less than equal to k
if (value <= k)
subset2.push_back(value);
}
// sort subset2
sort(subset2.begin(), subset2.end());
int count = 0;
for (int i = 0; i < subset1.size(); i++)
count += upper_bound(subset2.begin(), subset2.end(),
(k / subset1[i])) - subset2.begin();
// for null subset decrement the
// value of count
count--;
return count;
}
int main() {
vector<int> arr = {4, 2, 3, 6, 5};
int k = 25;
cout << numOfSubsets(arr, k);
return 0;
}
// A java program to find the count subset having product
// less than k
import java.util.*;
class GfG {
static int numOfSubsets(List<Integer> arr, int k) {
int n = arr.size();
// Declare four lists for dividing array into
// two halves and storing product values of
// possible subsets for them
List<Integer> vect1 = new ArrayList<>();
List<Integer> vect2 = new ArrayList<>();
List<Integer> subset1 = new ArrayList<>();
List<Integer> subset2 = new ArrayList<>();
// Ignore elements greater than k and divide
// array into 2 halves
for (int i = 0; i < n; i++) {
// Ignore element if greater than k
if (arr.get(i) > k)
continue;
if (i <= n / 2)
vect1.add(arr.get(i));
else
vect2.add(arr.get(i));
}
// Generate all subsets for 1st half (vect1)
for (int i = 0; i < (1 << vect1.size()); i++) {
int value = 1;
for (int j = 0; j < vect1.size() && value <= k;
j++) {
if ((i & (1 << j)) != 0)
value *= vect1.get(j);
}
// Add only if subset product is less than or
// equal to k
if (value <= k)
subset1.add(value);
}
// Generate all subsets for 2nd half (vect2)
for (int i = 0; i < (1 << vect2.size()); i++) {
int value = 1;
for (int j = 0; j < vect2.size() && value <= k;
j++) {
if ((i & (1 << j)) != 0)
value *= vect2.get(j);
}
// Add only if subset product is less than or
// equal to k
if (value <= k)
subset2.add(value);
}
// Sort subset2
Collections.sort(subset2);
int count = 0;
for (int s1 : subset1) {
count += upperBound(subset2, k / s1);
}
// For null subset decrement
// the value of count
count--;
return count;
}
// Helper function to find the upper bound index
static int upperBound(List<Integer> list, int value) {
int low = 0, high = list.size();
while (low < high) {
int mid = (low + high) / 2;
if (list.get(mid) <= value) {
low = mid + 1;
}
else {
high = mid;
}
}
return low;
}
public static void main(String[] args) {
List<Integer> arr = Arrays.asList(4, 2, 3, 6, 5);
int k = 25;
System.out.println(numOfSubsets(arr, k));
}
}
# Python to find the count subset
# having product less than k
import bisect
def findSubset(arr, k):
# declare four vector for dividing
# array into two halves and storing
# product value of possible subsets
# for them
n = len(arr)
vect1, vect2, subset1, subset2 = [], [], [], []
# ignore element greater than k and
# divide array into 2 halves
for i in range(0, n):
# ignore element if greater than k
if arr[i] > k:
continue
if i <= n // 2:
vect1.append(arr[i])
else:
vect2.append(arr[i])
# generate all subsets for 1st half (vect1)
for i in range(0, (1 << len(vect1))):
value = 1
for j in range(0, len(vect1)):
if i & (1 << j):
value *= vect1[j]
# push only in case subset product
# is less than equal to k
if value <= k:
subset1.append(value)
# generate all subsets for 2nd half (vect2)
for i in range(0, (1 << len(vect2))):
value = 1
for j in range(0, len(vect2)):
if i & (1 << j):
value *= vect2[j]
# push only in case subset product
# is less than equal to k
if value <= k:
subset2.append(value)
# sort subset2
subset2.sort()
count = 0
for i in range(0, len(subset1)):
count += bisect.bisect(subset2, (k // subset1[i]))
# for null subset decrement the
# value of count
count -= 1
return count
if __name__ == "__main__":
arr = [4, 2, 3, 6, 5]
k = 25
print(findSubset(arr, k))
// A C# program to find the count subset having product
// less than k
using System;
using System.Collections.Generic;
class GfG {
// Function to find the count of subsets having product
// less than k
static int numOfSubsets(List<int> arr, int k) {
int n = arr.Count;
// Declare lists for dividing array into
// two halves and storing product values of
// possible subsets for them
List<int> vect1 = new List<int>();
List<int> vect2 = new List<int>();
List<int> subset1 = new List<int>();
List<int> subset2 = new List<int>();
// Ignore elements greater than k and divide
// array into 2 halves
for (int i = 0; i < n; i++) {
if (arr[i] > k)
continue;
if (i <= n / 2)
vect1.Add(arr[i]);
else
vect2.Add(arr[i]);
}
// Generate all subsets for 1st half (vect1)
for (int i = 0; i < (1 << vect1.Count); i++) {
int value = 1;
for (int j = 0; j < vect1.Count && value <= k;
j++) {
if ((i & (1 << j)) != 0)
value *= vect1[j];
}
if (value <= k)
subset1.Add(value);
}
// Generate all subsets for 2nd half (vect2)
for (int i = 0; i < (1 << vect2.Count); i++) {
int value = 1;
for (int j = 0; j < vect2.Count && value <= k;
j++) {
if ((i & (1 << j)) != 0)
value *= vect2[j];
}
if (value <= k)
subset2.Add(value);
}
// Sort subset2
subset2.Sort();
int count = 0;
foreach(int value in subset1) {
count += BinarySearchUpperBound(subset2,
k / value);
}
// For null subset decrement
// the value of count
count--;
return count;
}
// Binary search to find the upper bound of value in
// subset2
static int BinarySearchUpperBound(List<int> list,
int value) {
int low = 0, high = list.Count;
while (low < high) {
int mid = (low + high) / 2;
if (list[mid] <= value)
low = mid + 1;
else
high = mid;
}
return low;
}
static void Main(string[] args) {
List<int> arr = new List<int>{ 4, 2, 3, 6, 5 };
int k = 25;
Console.WriteLine(numOfSubsets(arr, k));
}
}
// A Javascript program to find the count subset having
// product less than k
function binarySearchUpperBound(arr, value) {
let low = 0, high = arr.length;
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (arr[mid] <= value) {
low = mid + 1;
}
else {
high = mid;
}
}
return low;
}
function numOfSubsets(arr, k) {
let n = arr.length;
// Declare lists for dividing array into
// two halves and storing product values of
// possible subsets for them
let vect1 = [];
let vect2 = [];
let subset1 = [];
let subset2 = [];
// Ignore elements greater than k and divide
// array into 2 halves
for (let i = 0; i < n; i++) {
if (arr[i] > k)
continue;
if (i <= Math.floor(n / 2)) {
vect1.push(arr[i]);
}
else {
vect2.push(arr[i]);
}
}
// Generate all subsets for 1st half (vect1)
for (let i = 0; i < (1 << vect1.length); i++) {
let value = 1;
for (let j = 0; j < vect1.length && value <= k;
j++) {
if (i & (1 << j)) {
value *= vect1[j];
}
}
if (value <= k) {
subset1.push(value);
}
}
// Generate all subsets for 2nd half (vect2)
for (let i = 0; i < (1 << vect2.length); i++) {
let value = 1;
for (let j = 0; j < vect2.length && value <= k;
j++) {
if (i & (1 << j)) {
value *= vect2[j];
}
}
if (value <= k) {
subset2.push(value);
}
}
// Sort subset2
subset2.sort((a, b) => a - b);
let count = 0;
for (let value of subset1) {
count += binarySearchUpperBound(
subset2, Math.floor(k / value));
}
// For null subset decrement
// the value of count
count--;
return count;
}
const arr = [ 4, 2, 3, 6, 5 ];
const k = 25;
console.log(numOfSubsets(arr, k));
Output
15
Please refer to Number of subsets with product less than k using DP for Dynamic Programming solution.