Given an integer array arr[] and an integer k, the task is to check if it is possible to divide the given array into k non-empty subsets of equal sum such that every array element is part of a single subset.
Examples:
Input: arr[] = [2, 1, 4, 5, 6], k = 3
Output: true
Explanation: Possible subsets of the given array are [2, 4], [1, 5] and [6]Input: arr[] = [2, 1, 5, 5, 6], k = 3
Output: false
Explanation: It is not possible to divide above array into 3 parts with equal sum.
Table of Content
Using Recursion - O(k*2^n) Time and O(n*k) Space
We recursively explore all possible combinations for each of the k subsets. This is achieved by tracking the sum of the current subset and using a boolean array (taken) to check if an element has already been included in a subset or not.
Base Cases:
- if k = 1, the entire array forms the only subset.
- if n < k, it is impossible to divide the array into k subset since there are not enough elements.
- if the total sum of array is not divisible by k, equal partitioning is not feasible.
if these condition are met, the task reduces to dividing the array into k subsets , each with sum equal to arraySum/k.
Recursive Cases:
The recursive function attempts to add elements to each subset:
- If a subset’s sum matches the required value, the function moves to the next subset.
- If a subset doesn’t reach the target, the function backtracks and tries other combinations of elements.
- Once k-1 subsets reach the required sum, the remaining elements automatically form the final subset with the desired sum, confirming that partitioning is possible.
Steps
- Calculate total sum and check base conditions (
k == 1,n < k,sum % k != 0) - Set target =
sum / kand initializetaken[]andsubsetSum[] - Use recursion to try adding unused elements to the current subset while keeping sum ≤ target
- If a subset reaches target, move to the next subset; if
k-1subsets are formed, return true - Apply backtracking (undo choices) if no valid combination works, and finally return false if partition is not possible.
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
bool isKPartitionPossible(vector<int> &arr, vector<int> &subsetSum,
vector<bool> &taken, int target, int k,
int n, int currIdx, int limitIdx) {
// If the current subset sum matches the target
if (subsetSum[currIdx] == target) {
// If all but one subset are filled, the
// last subset is guaranteed to work
if (currIdx == k - 2)
return true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx + 1, n - 1);
}
for (int i = limitIdx; i >= 0; i--) {
if (taken[i])
continue;
int temp = subsetSum[currIdx] + arr[i];
if (temp <= target) {
// Only proceed if it doesn't exceed the target
taken[i] = true;
subsetSum[currIdx] += arr[i];
if (isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx, i - 1))
return true;
// Backtrack
taken[i] = false;
subsetSum[currIdx] -= arr[i];
}
}
return false;
}
bool isPartK(vector<int> &arr, int k) {
int n = arr.size(), sum = accumulate(arr.begin(), arr.end(), 0);
// If only one subset is needed, it's always possible
if (k == 1)
return true;
// Check if partition is impossible
if (n < k || sum % k != 0)
return false;
int target = sum / k;
vector<int> subsetSum(k, 0);
vector<bool> taken(n, false);
// Initialize first subset with the last element
subsetSum[0] = arr[n - 1];
taken[n - 1] = true;
// Recursively check for partitions
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, 0, n - 1);
}
int main() {
vector<int> arr = {2, 1, 4, 5, 3, 3};
int k = 3;
if (isPartK(arr, k))
cout << "true";
else
cout << "false";
return 0;
}
import java.util.*;
class GFG {
static boolean isKPartitionPossible(int[] arr, int[] subsetSum,
boolean[] taken, int target, int k,
int n, int currIdx, int limitIdx) {
// If the current subset sum matches the target
if (subsetSum[currIdx] == target) {
// If all but one subset are filled, last will be valid
if (currIdx == k - 2)
return true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx + 1, n - 1);
}
for (int i = limitIdx; i >= 0; i--) {
if (taken[i])
continue;
int temp = subsetSum[currIdx] + arr[i];
if (temp <= target) {
// Choose element
taken[i] = true;
subsetSum[currIdx] += arr[i];
if (isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx, i - 1))
return true;
// Backtrack
taken[i] = false;
subsetSum[currIdx] -= arr[i];
}
}
return false;
}
static boolean isPartK(int[] arr, int k) {
int n = arr.length;
int sum = 0;
for (int x : arr)
sum += x;
// If only one subset is needed
if (k == 1)
return true;
// Invalid case
if (n < k || sum % k != 0)
return false;
int target = sum / k;
int[] subsetSum = new int[k];
boolean[] taken = new boolean[n];
// Initialize first subset with last element
subsetSum[0] = arr[n - 1];
taken[n - 1] = true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, 0, n - 1);
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 5, 3, 3};
int k = 3;
if (isPartK(arr, k))
System.out.println("true");
else
System.out.println("false");
}
}
from functools import reduce
def isKPartitionPossible(arr, subsetSum, taken, target, k, n, currIdx, limitIdx):
# If the current subset sum matches the target
if subsetSum[currIdx] == target:
# If all but one subset are filled, the
# last subset is guaranteed to work
if currIdx == k - 2:
return True
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx + 1, n - 1)
for i in range(limitIdx, -1, -1):
if taken[i]:
continue
temp = subsetSum[currIdx] + arr[i]
if temp <= target:
# Only proceed if it doesn't exceed the target
taken[i] = True
subsetSum[currIdx] += arr[i]
if isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx, i - 1):
return True
# Backtrack
taken[i] = False
subsetSum[currIdx] -= arr[i]
return False
def isPartK(arr, k):
n = len(arr)
sum_val = reduce(lambda x, y: x + y, arr)
# If only one subset is needed, it's always possible
if k == 1:
return True
# Check if partition is impossible
if n < k or sum_val % k!= 0:
return False
target = sum_val // k
subsetSum = [0] * k
taken = [False] * n
# Initialize first subset with the last element
subsetSum[0] = arr[n - 1]
taken[n - 1] = True
# Recursively check for partitions
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, 0, n - 1)
if __name__ == '__main__':
arr = [2, 1, 4, 5, 3, 3]
k = 3
if isPartK(arr, k):
print('true')
else:
print('false')
using System;
class GFG {
static bool isKPartitionPossible(int[] arr, int[] subsetSum,
bool[] taken, int target, int k,
int n, int currIdx, int limitIdx) {
// If the current subset sum matches the target
if (subsetSum[currIdx] == target) {
// If all but one subset are filled
if (currIdx == k - 2)
return true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx + 1, n - 1);
}
for (int i = limitIdx; i >= 0; i--) {
if (taken[i])
continue;
int temp = subsetSum[currIdx] + arr[i];
if (temp <= target) {
// Choose element
taken[i] = true;
subsetSum[currIdx] += arr[i];
if (isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx, i - 1))
return true;
// Backtrack
taken[i] = false;
subsetSum[currIdx] -= arr[i];
}
}
return false;
}
static bool isPartK(int[] arr, int k) {
int n = arr.Length;
int sum = 0;
foreach (int x in arr)
sum += x;
// If only one subset is needed
if (k == 1)
return true;
// Invalid case
if (n < k || sum % k != 0)
return false;
int target = sum / k;
int[] subsetSum = new int[k];
bool[] taken = new bool[n];
// Initialize first subset with last element
subsetSum[0] = arr[n - 1];
taken[n - 1] = true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, 0, n - 1);
}
public static void Main() {
int[] arr = {2, 1, 4, 5, 3, 3};
int k = 3;
if (isPartK(arr, k))
Console.WriteLine("true");
else
Console.WriteLine("false");
}
}
function isKPartitionPossible(arr, subsetSum, taken, target, k, n, currIdx, limitIdx) {
// If the current subset sum matches the target
if (subsetSum[currIdx] === target) {
// If all but one subset are filled, the
// last subset is guaranteed to work
if (currIdx === k - 2)
return true;
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx + 1, n - 1);
}
for (let i = limitIdx; i >= 0; i--) {
if (taken[i])
continue;
let temp = subsetSum[currIdx] + arr[i];
if (temp <= target) {
// Only proceed if it doesn't exceed the target
taken[i] = true;
subsetSum[currIdx] += arr[i];
if (isKPartitionPossible(arr, subsetSum, taken,
target, k, n, currIdx, i - 1))
return true;
// Backtrack
taken[i] = false;
subsetSum[currIdx] -= arr[i];
}
}
return false;
}
function isPartK(arr, k) {
let n = arr.length, sum = arr.reduce((a, b) => a + b, 0);
// If only one subset is needed, it's always possible
if (k === 1)
return true;
// Check if partition is impossible
if (n < k || sum % k!== 0)
return false;
let target = Math.floor(sum / k);
let subsetSum = new Array(k).fill(0);
let taken = new Array(n).fill(false);
// Initialize first subset with the last element
subsetSum[0] = arr[n - 1];
taken[n - 1] = true;
// Recursively check for partitions
return isKPartitionPossible(arr, subsetSum, taken,
target, k, n, 0, n - 1);
}
(function() {
let arr = [2, 1, 4, 5, 3, 3];
let k = 3;
if (isPartK(arr, k))
console.log('true');
else
console.log('false');
})();
Output
true
Using Bitmasking and DP - O(n*2^n) and O(2^n) Space
The idea is to use mask to determine the current state. The current state tells us about the subset already formed (which numbers are already selected).
For example: arr[] = [2, 1, 4, 3, 5, 6, 2], mask = (1100101), which means that [2, 1, 5, 2] are already chosen in the current mask.
For any current state mask, the jth element will be added to it based on the following two conditions:
- The jth bit is not set in the mask (mask & (1<<j) == 0)
- sum (mask) + arr[j] <= target ( where target = (sum of array elements) / k)
Maintain a table dp[] such that dp[i] store the sum of elements in mask i. So, the dp transitions will be:
dp[i | (1 << j)] = (dp[i] + arr[j]) % target
- Calculate sum, check divisibility by
k, set target - Use
dp[mask]to store current subset sum, initializedp[0] = 0 - For each mask, try adding unused element
jif sum ≤ target - Update:
dp[newMask] = (dp[mask] + arr[j]) % target - Continue till all masks are processed
- If final mask is valid → return true, else false
#include <iostream>
#include <vector>
using namespace std;
bool isPartK(vector<int> &arr, int k) {
int n = arr.size();
// Return true as the entire array is the answer
if (k == 1)
return true;
if (n < k)
return false;
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
if (sum % k != 0)
// No such partitions are possible
return false;
int target = sum / k;
// Initialize dp vector with -1
vector<int> dp(1 << n, -1);
// Sum of empty subset is zero
dp[0] = 0;
// Iterate over all subsets/masks
for (int mask = 0; mask < (1 << n); mask++) {
// if current mask is invalid, continue
if (dp[mask] == -1)
continue;
// Iterate over all array elements
for (int i = 0; i < n; i++) {
// Check if the current element can be added
// to the current subset/mask
if (!(mask & (1 << i)) && dp[mask] + arr[i] <= target) {
dp[mask | (1 << i)] = (dp[mask] + arr[i]) % target;
}
}
}
// If the dp value of all elements used is zero, then
// partitioning is possible
return dp[(1 << n) - 1] == 0;
}
int main() {
vector<int> arr = {2, 1, 4, 5, 3, 3};
int k = 2;
if (isPartK(arr, k)) {
cout << "true";
}
else {
cout << "false";
}
}
import java.util.*;
class GFG {
static boolean isPartK(int[] arr, int k) {
int n = arr.length;
if (k == 1)
// Return true as the entire array is the answer
return true;
if (n < k)
return false;
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
if (sum % k != 0)
// No such partitions are possible
return false;
int target = sum / k;
// Initialize dp array with -1
int[] dp = new int[1 << n];
Arrays.fill(dp, -1);
// Sum of empty subset is zero
dp[0] = 0;
// Iterate over all subsets/masks
for (int mask = 0; mask < (1 << n); mask++) {
// if current mask is invalid, continue
if (dp[mask] == -1)
continue;
// Iterate over all array elements
for (int i = 0; i < n; i++) {
// Check if the current element can be added
// to the current subset/mask
if ((mask & (1 << i)) == 0 && dp[mask] + arr[i] <= target) {
dp[mask | (1 << i)] = (dp[mask] + arr[i]) % target;
}
}
}
// If all elements used and remainder is zero
return dp[(1 << n) - 1] == 0;
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 5, 3, 3};
int k = 2;
if (isPartK(arr, k))
System.out.println("true");
else
System.out.println("false");
}
}
def isPartK(arr, k):
n = len(arr)
if k == 1:
# Return true as the entire array is the answer
return True
if n < k:
return False
sum_val = sum(arr)
if sum_val % k!= 0:
# No such partitions are possible
return False
target = sum_val // k
# Initialize dp list with -1
dp = [-1] * (1 << n)
# Sum of empty subset is zero
dp[0] = 0
# Iterate over all subsets/masks
for mask in range(1 << n):
# if current mask is invalid, continue
if dp[mask] == -1:
continue
# Iterate over all array elements
for i in range(n):
# Check if the current element can be added
# to the current subset/mask
if not (mask & (1 << i)) and dp[mask] + arr[i] <= target:
dp[mask | (1 << i)] = (dp[mask] + arr[i]) % target
# If the dp value of all elements used is zero, then
# partitioning is possible
return dp[(1 << n) - 1] == 0
if __name__ == '__main__':
arr = [2, 1, 4, 5, 3, 3]
k = 2
if isPartK(arr, k):
print('true')
else:
print('false')
using System;
class GFG {
static bool isPartK(int[] arr, int k) {
int n = arr.Length;
if (k == 1)
// Return true as the entire array is the answer
return true;
if (n < k)
return false;
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
if (sum % k != 0)
// No such partitions are possible
return false;
int target = sum / k;
// Initialize dp array with -1
int[] dp = new int[1 << n];
for (int i = 0; i < dp.Length; i++)
dp[i] = -1;
// Sum of empty subset is zero
dp[0] = 0;
// Iterate over all subsets/masks
for (int mask = 0; mask < (1 << n); mask++) {
// if current mask is invalid, continue
if (dp[mask] == -1)
continue;
// Iterate over all array elements
for (int i = 0; i < n; i++) {
// Check if the current element can be added
// to the current subset/mask
if ((mask & (1 << i)) == 0 && dp[mask] + arr[i] <= target) {
dp[mask | (1 << i)] = (dp[mask] + arr[i]) % target;
}
}
}
// If all elements used and remainder is zero
return dp[(1 << n) - 1] == 0;
}
public static void Main() {
int[] arr = {2, 1, 4, 5, 3, 3};
int k = 2;
if (isPartK(arr, k))
Console.WriteLine("true");
else
Console.WriteLine("false");
}
}
function isPartK(arr, k) {
let n = arr.length;
if (k === 1)
// Return true as the entire array is the answer
return true;
if (n < k)
return false;
let sum = arr.reduce((a, b) => a + b, 0);
if (sum % k!== 0)
// No such partitions are possible
return false;
let target = Math.floor(sum / k);
// Initialize dp array with -1
let dp = new Array(1 << n).fill(-1);
// Sum of empty subset is zero
dp[0] = 0;
// Iterate over all subsets/masks
for (let mask = 0; mask < (1 << n); mask++) {
// if current mask is invalid, continue
if (dp[mask] === -1)
continue;
// Iterate over all array elements
for (let i = 0; i < n; i++) {
// Check if the current element can be added
// to the current subset/mask
if ((mask & (1 << i)) === 0 && dp[mask] + arr[i] <= target) {
dp[mask | (1 << i)] = (dp[mask] + arr[i]) % target;
}
}
}
// If the dp value of all elements used is zero, then
// partitioning is possible
return dp[(1 << n) - 1] === 0;
}
function main() {
let arr = [2, 1, 4, 5, 3, 3];
let k = 2;
if (isPartK(arr, k)) {
console.log('true');
} else {
console.log('false');
}
}
main();
Output
true