Given an integer array A[] of size N and two integers K, D. A list or superset of all possible subset sums of size K is made from the given array, the task is to find the largest multiple of D from this list or superset. If there exists no multiple, return -1 as the answer.
Examples:
Input: N = 4, K = 2, D = 2, A[] = [1, 2, 3, 4]
Output: 6
Explanation: All possible sums are [3, 4, 5, 5, 6, 7]. The greatest multiple of 2 is 6.Input: N = 4, K = 1, D = 3, A[] = [1, 5, 7, 8]
Output: -1
Explanation: All possible sums are [1, 5, 7, 8]. No multiple of 3 exists, hence return -1 as the answer.
Approach: To solve the problem using Recursive Top-Down Dp follow the below idea::
The main idea behind this solution is to break down the problem into smaller subproblems by considering each element of the array A. At each index i of the array A, we have two options – either include the element in one of the K subsets or exclude it. Thus, we can represent the problem as a tree of decisions, with each node representing the inclusion or exclusion of an element.
Steps that were to follow to implement the above approach:
-
The countSubsetsRec function recursively calculates the maximum sum of a subset of a that contains exactly j elements and has a sum that is a multiple of D, where a is the input array of size N. The function takes the current index i, the number of chosen elements j, the current sum modulo D k, and the memoization table dp.
- The base case is when we have processed all N elements. If we have chosen exactly K elements and the sum is a multiple of D, we return 0, as we have found a valid subset. Otherwise, we return -INF to indicate that this state is impossible.
- We then check if the current state is already computed in the memoization table dp. If it is, we return the stored value.
-
Otherwise, we calculate the maximum sum of a subset that contains exactly j elements and has a sum that is a multiple of D using the two possible transitions –
- one where we do not choose the current element and,
- one where we choose it.
- We take the maximum of these two transitions.
- Finally, we memoize the current state and return the result.
- The countSubsets function initializes the memoization table dp and calls countSubsetsRec with the initial parameters.
Below is the code to implement the above approach:
// C++ code for the above approach. #include <bits/stdc++.h> using namespace std;
// Main recursive function to return the // count of such subsets. long long
countSubsetsRec( int i, int j, int k, int N, int K, int D,
vector< int >& a,
vector<vector<vector< long long > > >& dp)
{ // Base case
if (i == N) {
if (j == K && k == 0) {
return 0;
}
else {
return INT_MIN;
}
}
// Check if the current state
// is already computed
if (dp[i][j][k] != -1) {
return dp[i][j][k];
}
// Transition when A[i] isn't chosen
long long ans
= countSubsetsRec(i + 1, j, k, N, K, D, a, dp);
// Transition when A[i] is chosen
if (j != K) {
ans = max(ans, countSubsetsRec(i + 1, j + 1,
(k + a[i]) % D, N, K,
D, a, dp)
+ a[i]);
}
// Memoize the current state
dp[i][j][k] = ans;
return ans;
} long long countSubsets( int N, int K, int D, vector< int > a)
{ // Initializing the dp array
vector<vector<vector< long long > > > dp(
N + 1, vector<vector< long long > >(
K + 1, vector< long long >(D, -1)));
// Call the recursive function
// and return the answer
return countSubsetsRec(0, 0, 0, N, K, D, a, dp);
} // Driver's code int main()
{ int N = 4, K = 2, D = 2;
vector< int > a = { 1, 2, 3, 4 };
if (countSubsets(N, K, D, a) < 0) {
cout << -1;
return 0;
}
// Function call
cout << countSubsets(N, K, D, a);
return 0;
} |
// Java code for the above approach. import java.io.*;
import java.util.*;
class GFG {
// main recursive function to return the count of such
// subsets.
static long countSubsetsRec( int i, int j, int k, int N,
int K, int D, int [] a,
long [][][] dp)
{
// Base case
if (i == N) {
if (j == K && k == 0 ) {
return 0 ;
}
else {
return Integer.MIN_VALUE;
}
}
// Check if the current state is already computed
if (dp[i][j][k] != - 1 ) {
return dp[i][j][k];
}
// Transition when A[i] isn't chosen
long ans
= countSubsetsRec(i + 1 , j, k, N, K, D, a, dp);
// Transition when A[i] is chosen
if (j != K) {
ans = Math.max(ans,
countSubsetsRec(i + 1 , j + 1 ,
(k + a[i]) % D,
N, K, D, a, dp)
+ a[i]);
}
// Memoize the current state
dp[i][j][k] = ans;
// Return the answer.
return ans;
}
static long countSubsets( int N, int K, int D, int [] a)
{
// Initializing the dp array
long [][][] dp = new long [N + 1 ][K + 1 ][D];
for ( int i = 0 ; i <= N; i++) {
for ( int j = 0 ; j <= K; j++) {
Arrays.fill(dp[i][j], - 1 );
}
}
// Call the recursive function and return the answer
long ans = countSubsetsRec( 0 , 0 , 0 , N, K, D, a, dp);
if (ans < 0 ) {
return - 1 ;
}
else {
return ans;
}
}
public static void main(String[] args)
{
int N = 4 , K = 2 , D = 2 ;
int [] a = { 1 , 2 , 3 , 4 };
if (countSubsets(N, K, D, a) < 0 ) {
System.out.println( "-1" );
return ;
}
// Function call
System.out.println(countSubsets(N, K, D, a));
}
} // This code is contributed by sankar. |
# Python code for the above approach # Main recursive function to return the # count of such subsets. def countSubsetsRec(i, j, k, N, K, D, a, dp):
# Base case
if i = = N:
if j = = K and k = = 0 :
return 0
else :
return float ( '-inf' )
# Check if the current state
# is already computed
if dp[i][j][k] ! = - 1 :
return dp[i][j][k]
# Transition when A[i] isn't chosen
ans = countSubsetsRec(i + 1 , j, k, N, K, D, a, dp)
# Transition when A[i] is chosen
if j ! = K:
ans = max (ans, countSubsetsRec(i + 1 , j + 1 , (k + a[i]) % D, N, K, D, a, dp) + a[i])
# Memoize the current state
dp[i][j][k] = ans
return ans
def countSubsets(N, K, D, a):
# Initializing the dp array
dp = [[[ - 1 for i in range (D)] for j in range (K + 1 )] for k in range (N + 1 )]
# Call the recursive function
# and return the answer
ans = countSubsetsRec( 0 , 0 , 0 , N, K, D, a, dp)
if ans < 0 :
return - 1
else :
return ans
# Driver's code if __name__ = = '__main__' :
N = 4
K = 2
D = 2
a = [ 1 , 2 , 3 , 4 ]
print (countSubsets(N, K, D, a))
# This Code is Contributed by Shivam Tiwari |
// C# program to find the maximum sum of a // subset such that no two elements in the // subset have a common factor greater than 1. using System;
class GFG {
// Function to return the maximum sum of a
// subset such that no two elements in the
// subset have a common factor greater than 1.
static long countSubsets( int N, int K, int D, int [] a)
{
// Initializing the dp array
long [, , ] dp = new long [N + 1, K + 1, D];
for ( int i = 0; i <= N; i++) {
for ( int j = 0; j <= K; j++) {
for ( int k = 0; k < D; k++) {
dp[i, j, k] = -1;
}
}
}
// Call the recursive function and return the answer
long ans = countSubsetsRec(0, 0, 0, N, K, D, a, dp);
if (ans < 0) {
return -1;
}
else {
return ans;
}
}
// main recursive function to return the count of such
// subsets.
static long countSubsetsRec( int i, int j, int k, int N,
int K, int D, int [] a,
long [, , ] dp)
{
// Base case
if (i == N) {
if (j == K && k == 0) {
return 0;
}
else {
return Int32.MinValue;
}
}
// Check if the current state is already computed
if (dp[i, j, k] != -1) {
return dp[i, j, k];
}
// Transition when A[i] isn't chosen
long ans
= countSubsetsRec(i + 1, j, k, N, K, D, a, dp);
// Transition when A[i] is chosen
if (j != K) {
ans = Math.Max(ans,
countSubsetsRec(i + 1, j + 1,
(k + a[i]) % D,
N, K, D, a, dp)
+ a[i]);
}
// Memoize the current state
dp[i, j, k] = ans;
// Return the answer.
return ans;
}
// Driver code
public static void Main()
{
int N = 4, K = 2, D = 2;
int [] a = { 1, 2, 3, 4 };
if (countSubsets(N, K, D, a) < 0) {
Console.WriteLine( "-1" );
return ;
}
// Function call
Console.WriteLine(countSubsets(N, K, D, a));
}
} // This code is contributed by Tapesh(tapeshdua420) |
// Main recursive function to return the // count of such subsets. function countSubsetsRec(i, j, k, N, K, D, a, dp) {
// Base case
if (i === N) {
if (j === K && k === 0) {
return 0;
} else {
return Number.MIN_SAFE_INTEGER;
}
}
// Check if the current state
// is already computed
if (dp[i][j][k] !== -1) {
return dp[i][j][k];
}
// Transition when A[i] isn't chosen
let ans = countSubsetsRec(i + 1, j, k, N, K, D, a, dp);
// Transition when A[i] is chosen
if (j !== K) {
ans = Math.max(ans, countSubsetsRec(i + 1, j + 1, (k + a[i]) % D, N, K, D, a, dp) + a[i]);
}
// Memoize the current state
dp[i][j][k] = ans;
return ans;
} function countSubsets(N, K, D, a) {
// Initializing the dp array
const dp = new Array(N + 1).fill( null ).map(() =>
new Array(K + 1).fill( null ).map(() => new Array(D).fill(-1))
);
// Call the recursive function
// and return the answer
return countSubsetsRec(0, 0, 0, N, K, D, a, dp);
} // Driver's code const N = 4, K = 2, D = 2;
const a = [1, 2, 3, 4];
if (countSubsets(N, K, D, a) < 0) {
console.log(-1);
}
// Function call
console.log(countSubsets(N, K, D, a));
|
6
Time Complexity: O(NKD), since we are doing nested iteration N*K*D times.
Auxiliary Space: O(NKD), for creating the dp array.
Approach: To solve the problem using the Bottom-up Dp approach follow the below idea:
Let’s define dp[i][j][k] as the maximum sum of j elements chosen out of A[1], A[2], ….., A[i] such that the remainder when the sum is divided by D is k (or −1 if it is impossible).
Now there can be two cases:
- Skip the current elements, we can update the dp as:
- dp[i + 1][j][k] = max(dp[i + 1][j][k], dp[i][j][k])
- Take the current element, we can update the dp as:
- dp[i + 1][j + 1][(k + a[i]) % D] = max(dp[i + 1][j + 1][(k + a[i]) % D], dp[i][j][k] + a[i])
The final answer is stored in state dp[n][k][0].
Steps that were to follow the above approach:
- Initialize a dp array of (N+1)*(K+1)*(D) size with all states as -1, and dp[0][0][0] = 0.
-
Now iterate for i in the range [0, N), for j in the range [0, K], and for k in the range [0, D).
- If dp[i][j][k] = -1, then this state is not reachable.
- Otherwise, we can do the transition as mentioned.
- For the second case where we have to choose the element, check if the number of chosen elements till now is not equal to K, i.e., j != K.
- Return the final answer as dp[n][k][0].
Below is the code to implement the above approach:
// C++ code for the above approach #include <bits/stdc++.h> using namespace std;
// Function to return the count of subsets // divisible by D, and of size K. int countSubsets( int N, int K, int D, vector< int > a)
{ // Initializing the dp array
long long dp[N + 1][K + 1][D];
// Setting all states to -1.
memset (dp, -1, sizeof (dp));
dp[0][0][0] = 0;
for ( int i = 0; i < N; i++) {
for ( int j = 0; j < K + 1; j++) {
for ( int k = 0; k < D; k++) {
// This state is not
// achievable.
if (dp[i][j][k] == -1)
continue ;
// Transition when A[i]
// isn't chosen
dp[i + 1][j][k]
= max(dp[i + 1][j][k], dp[i][j][k]);
// Transition when A[i]
// is chosen
if (j != K) {
dp[i + 1][j + 1][(k + a[i]) % D] = max(
dp[i + 1][j + 1][(k + a[i]) % D],
dp[i][j][k] + a[i]);
}
}
}
}
// Returning the answer.
return dp[N][K][0];
} // Driver's code int main()
{ int N = 4, K = 2, D = 2;
vector< int > a = { 1, 2, 3, 4 };
// Function call
cout << countSubsets(N, K, D, a);
return 0;
} |
import java.util.Arrays;
import java.util.Vector;
public class Main {
// Function to return the count of subsets
// divisible by D, and of size K.
static int countSubsets( int N, int K, int D, Vector<Integer> a) {
// Initializing the dp array
long [][][] dp = new long [N + 1 ][K + 1 ][D];
// Setting all states to -1.
for ( long [][] layer1 : dp) {
for ( long [] layer2 : layer1) {
Arrays.fill(layer2, - 1 );
}
}
dp[ 0 ][ 0 ][ 0 ] = 0 ;
for ( int i = 0 ; i < N; i++) {
for ( int j = 0 ; j < K + 1 ; j++) {
for ( int k = 0 ; k < D; k++) {
// This state is not
// achievable.
if (dp[i][j][k] == - 1 )
continue ;
// Transition when A[i]
// isn't chosen
dp[i + 1 ][j][k] = Math.max(dp[i + 1 ][j][k], dp[i][j][k]);
// Transition when A[i]
// is chosen
if (j != K) {
dp[i + 1 ][j + 1 ][(k + a.get(i)) % D] = Math.max(
dp[i + 1 ][j + 1 ][(k + a.get(i)) % D],
dp[i][j][k] + a.get(i));
}
}
}
}
// Returning the answer.
return ( int ) dp[N][K][ 0 ];
}
// Driver's code
public static void main(String[] args) {
int N = 4 , K = 2 , D = 2 ;
Vector<Integer> a = new Vector<>(Arrays.asList( 1 , 2 , 3 , 4 ));
// Function call
System.out.println(countSubsets(N, K, D, a));
}
} |
# Python code for the above approach def countSubsets(N, K, D, a):
# Initializing the dp array
dp = [[[ - 1 for _ in range (D)] for _ in range (K + 1 )] for _ in range (N + 1 )]
dp[ 0 ][ 0 ][ 0 ] = 0
for i in range (N):
for j in range (K + 1 ):
for k in range (D):
# This state is not achievable.
if dp[i][j][k] = = - 1 :
continue
# Transition when A[i] isn't chosen
dp[i + 1 ][j][k] = max (dp[i + 1 ][j][k], dp[i][j][k])
# Transition when A[i] is chosen
if j ! = K:
dp[i + 1 ][j + 1 ][(k + a[i]) % D] = max (dp[i + 1 ][j + 1 ]
[(k + a[i]) % D], dp[i][j][k] + a[i])
# Returning the answer
return dp[N][K][ 0 ]
# Driver's code N = 4
K = 2
D = 2
a = [ 1 , 2 , 3 , 4 ]
# Function call print (countSubsets(N, K, D, a))
# This code is contributed by Sakshi |
// C# code for the above approach using System;
class Program
{ // Function to return the count of subsets
// divisible by D, and of size K.
static int CountSubsets( int N, int K, int D, int [] a)
{
// Initializing the dp array
long [,,] dp = new long [N + 1, K + 1, D];
// Setting all states to -1.
for ( int i = 0; i <= N; i++)
{
for ( int j = 0; j <= K; j++)
{
for ( int k = 0; k < D; k++)
{
dp[i, j, k] = -1;
}
}
}
dp[0, 0, 0] = 0;
for ( int i = 0; i < N; i++)
{
for ( int j = 0; j < K + 1; j++)
{
for ( int k = 0; k < D; k++)
{
// This state is not
// achievable.
if (dp[i, j, k] == -1)
continue ;
// Transition when A[i]
// isn't chosen
dp[i + 1, j, k]
= Math.Max(dp[i + 1, j, k], dp[i, j, k]);
// Transition when A[i]
// is chosen
if (j != K)
{
dp[i + 1, j + 1, (k + a[i]) % D] = Math.Max(
dp[i + 1, j + 1, (k + a[i]) % D],
dp[i, j, k] + a[i]);
}
}
}
}
// Returning the answer.
return ( int )dp[N, K, 0];
}
// Driver's code
static void Main( string [] args)
{
int N = 4, K = 2, D = 2;
int [] a = { 1, 2, 3, 4 };
// Function call
Console.WriteLine(CountSubsets(N, K, D, a));
}
} |
function countSubsets(N, K, D, a) {
// Initialize a 3D array for dynamic programming
// dp[i][j][k] represents the maximum sum of the
// first i elements by choosing j elements that
// are divisible by D and the sum of those elements
// modulo D is k.
let dp = new Array(N + 1);
for (let i = 0; i <= N; i++) {
dp[i] = new Array(K + 1);
for (let j = 0; j <= K; j++) {
dp[i][j] = new Array(D);
for (let k = 0; k < D; k++) {
dp[i][j][k] = -1;
}
}
}
// Set the initial state where no elements are chosen
dp[0][0][0] = 0;
for (let i = 0; i < N; i++) {
for (let j = 0; j <= K; j++) {
for (let k = 0; k < D; k++) {
// This state is not achievable.
if (dp[i][j][k] === -1) {
continue ;
}
// Transition when a[i] isn't chosen
dp[i + 1][j][k] = Math.max(dp[i + 1][j][k], dp[i][j][k]);
// Transition when a[i] is chosen
if (j !== K) {
dp[i + 1][j + 1][(k + a[i]) % D] = Math.max(
dp[i + 1][j + 1][(k + a[i]) % D],
dp[i][j][k] + a[i]
);
}
}
}
}
// Return the answer from the last state
return dp[N][K][0];
} // Driver code let N = 4; let K = 2; let D = 2; let a = [1, 2, 3, 4]; // Function call console.log(countSubsets(N, K, D, a)); |
6
Time Complexity: O(N*K*D), since we are doing nested iteration N*K*D times.
Auxiliary Space: O(N*K*D), for creating the dp array.
Related Articles: