Given an array arr[] consisting of N integers and an integer K, the task is to find the minimum sum of incompatibilities of K subsets of equal sizes having unique elements.
The difference between the maximum and the minimum element in a set is known as the incompatibility of a set.
Examples:
Input: arr[] = {1, 2, 1, 4}, K = 2
Output: 4
Explanation:
One of the possible ways of distributing the array into K(i.e., 2) subsets is {1, 2} and {1, 4}.
The incompatibility of the first subset = (2 - 1) = 1.
The incompatibility of the second subset = (4 - 1) = 3.
Therefore, the total sum of incompatibilities of both subsets = 1 + 3 = 4, which is the minimum among all possibilities.Input: arr[] = {6, 3, 8, 1, 3, 1, 2, 2}, K = 4
Output: 6
Explanation:
One of the possible ways of distributing the array into K subset is: {1, 2}, {2, 3}, {6, 8}, {1, 3}.
The incompatibility of the first subset = (2-1) = 1.
The incompatibility of the second subset = (3-2) = 1.
The incompatibility of the third subset = (8-6) = 2.
The incompatibility of the fourth subset = (3-1) = 2.
Therefore, total sum of incompatibilities of K subset = 1 + 1 + 2 + 2 = 6. And it is also the minimum among all possibilities
Naive Approach: The simplest approach is to traverse the given array recursively and in each recursion traverse over all possible ways of selecting N/K elements of the array using bitmask and calculate the incompatibility of that subset and then return the minimum among all.
Time Complexity: O(N*23*N)
Auxiliary Space: O(N)
Efficient Approach: The above approach can be optimized using dynamic programming. The given problem can be solved based on the following observations:
- It can be observed that it requires a 2 state Dynamic programming with Bitmasking say DP(mask, i) to solve the problem where i represents the current position of the array and each binary bit of mask represent if the element has been already selected or not.
- The transition state will include calculating the incompatibility by selecting a subset of size N/K.
- Suppose X and Y is minimum and maximum of current set and newmask is another variable with value initially as the mask
- Now, mark all the N/K elements that have been selected occurs only once in newmask then DP(mask, i) is calculated by (Y - X + min(DP(newmask, i + 1), DP(mask, i))).
Follow the steps below to solve the problem:
- Initialize a 2D array, say dp[][].
- Define a recursive function, say dfs (mask, i), to calculate the result:
- Base Case: If i > K, then return 0.
- Check if dp[mask][i] != -1, then return dp[mask][i] as the current state is already calculated.
- Select N/K elements from the array using bitmasking and if it possible to choose i elements of the subset which only occurs once and are not already part of other subsets, then update the current dp state as:
dp[mask][i] = min(dp[mask][i], Y - X + dfs(newmask, i))
- Return the value of dp[mask][i] as the result in the current recursive call.
- Call the recursive function dfs(2N-1, 0) and print the value returned by it.
Below is the implementation of the above approach:
// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
int k;
int n;
int goal;
vector<vector<int>> dp;
vector<int> bits;
// Recursive function to find
// the minimum required value
int dfs(vector<int> A, int state,int index)
{
// Base Case
if (index >= k)
{
return 0;
}
// Stores the minimum value
// of the current state
int res = 1000;
// If dp[state][index] is
// already calculated
if (dp[state][index] != -1) {
// return dp[state][index]
return dp[state][index];
}
// Traverse over all the bits
for (int bit : bits) {
// If count of set bit is N/K
if (__builtin_popcount(bit)
== goal) {
// Store the new state after
// choosing N/K elements
int newstate = state;
// Stores the minimum and
// maximum of current subset
int mn = 100, mx = -1;
// Stores if the elements
// have been already
// selected or not
vector<bool> visit(n+1,false);
// Stores if it is possible
// to select N/K elements
bool good = true;
// Traverse over the array
for (int j = 0; j < n; j++) {
// If not chosen already
// for another subset
if ((bit & (1 << j)) != 0) {
// If already chosen
// for another subset
// or current subset
if (visit[A[j]] == true
|| (state & (1 << j)) == 0) {
// Mark the good false
good = false;
break;
}
// Mark the current
// number visited
visit[A[j]] = true;
// Mark the current
// position in mask
// newstate
newstate = newstate
^ (1 << j);
// Update the maximum
// and minimum
mx = max(mx,
A[j]);
mn = min(mn,
A[j]);
}
}
// If good is true then
// Update the res
if (good) {
res = min(
res, mx - mn
+ dfs(A, newstate,
index + 1));
}
}
}
// Update the current sp state
dp[state][index] = res;
// Return the res
return res;
}
// Function to find the minimum
// incompatibility sum
int minimumIncompatibility(vector<int> A, int K)
{
n = A.size();
k = K;
goal = n / k;
// Stores the count of element
map<int, int> mp;
// Traverse the array
for (int i : A) {
// If number i not occurs
// in Map
if (mp.find(i)==mp.end()){
// Put the element
// in the Map
mp[i] = 0;
}
// Increment the count of
// i in the Hash Map
mp[i]++;
// If count of i in Map is
// greater than K then
// return -1
if (mp[i] > k)
return -1;
}
// Stores all total state
int state = (1 << n) - 1;
// Traverse over all the state
for (int i = 0; i <= state; i++) {
// If number of set bit
// is equal to N/K
if (__builtin_popcount(i) == goal){
bits.push_back(i);
}
}
// Stores the minimum value
// at a state
dp.resize(1<<n,vector<int>(k,-1));
// Initialize the dp state
// with -1
// for (int i = 0;
// i < dp.ize(); i++) {
// Arrays.fill(dp[i], -1);
// }
// Call the recursive function
return dfs(A, state, 0);
}
// Driver code
int main()
{
vector<int> arr = { 1, 2, 1, 4 };
int K = 2;
// Function Call
cout<<(minimumIncompatibility(arr, K));
}
// This code is contributed by mohit kumar 29.
// Java program for the above approach
import java.io.*;
import java.util.*;
class Solution {
int k;
int n;
int goal;
int dp[][];
List<Integer> bits = new ArrayList<>();
// Function to find the minimum
// incompatibility sum
public int minimumIncompatibility(
int[] A, int k)
{
this.n = A.length;
this.k = k;
goal = n / k;
// Stores the count of element
Map<Integer, Integer> map
= new HashMap<>();
// Traverse the array
for (int i : A) {
// If number i not occurs
// in Map
if (!map.containsKey(i))
// Put the element
// in the Map
map.put(i, 0);
// Increment the count of
// i in the Hash Map
map.put(i, map.get(i) + 1);
// If count of i in Map is
// greater than K then
// return -1
if (map.get(i) > k)
return -1;
}
// Stores all total state
int state = (1 << n) - 1;
// Traverse over all the state
for (int i = 0; i <= state; i++) {
// If number of set bit
// is equal to N/K
if (Integer.bitCount(i) == goal)
bits.add(i);
}
// Stores the minimum value
// at a state
dp = new int[1 << n][k];
// Initialize the dp state
// with -1
for (int i = 0;
i < dp.length; i++) {
Arrays.fill(dp[i], -1);
}
// Call the recursive function
return dfs(A, state, 0);
}
// Recursive function to find
// the minimum required value
public int dfs(int A[], int state,
int index)
{
// Base Case
if (index >= k) {
return 0;
}
// Stores the minimum value
// of the current state
int res = 1000;
// If dp[state][index] is
// already calculated
if (dp[state][index] != -1) {
// return dp[state][index]
return dp[state][index];
}
// Traverse over all the bits
for (int bit : bits) {
// If count of set bit is N/K
if (Integer.bitCount(bit)
== goal) {
// Store the new state after
// choosing N/K elements
int newstate = state;
// Stores the minimum and
// maximum of current subset
int mn = 100, mx = -1;
// Stores if the elements
// have been already
// selected or not
boolean visit[]
= new boolean[n + 1];
// Stores if it is possible
// to select N/K elements
boolean good = true;
// Traverse over the array
for (int j = 0; j < n; j++) {
// If not chosen already
// for another subset
if ((bit & (1 << j)) != 0) {
// If already chosen
// for another subset
// or current subset
if (visit[A[j]] == true
|| (state & (1 << j)) == 0) {
// Mark the good false
good = false;
break;
}
// Mark the current
// number visited
visit[A[j]] = true;
// Mark the current
// position in mask
// newstate
newstate = newstate
^ (1 << j);
// Update the maximum
// and minimum
mx = Math.max(mx,
A[j]);
mn = Math.min(mn,
A[j]);
}
}
// If good is true then
// Update the res
if (good) {
res = Math.min(
res, mx - mn
+ dfs(A, newstate,
index + 1));
}
}
}
// Update the current sp state
dp[state][index] = res;
// Return the res
return res;
}
}
// Driver Code
class GFG {
public static void main(String[] args)
{
Solution st = new Solution();
int[] arr = { 1, 2, 1, 4 };
int K = 2;
// Function Call
System.out.print(
st.minimumIncompatibility(arr, K));
}
}
# Python program for the above approach:
k = 0
n = 0
goal = 0
dp = []
bits = []
def __builtin_popcount(n):
count = 0
while (n > 0):
count += (n & 1)
n >>= 1
return count
## Recursive function to find
## the minimum required value
def dfs(A, state, index):
global k
global n
global goal
global dp
global bits
## Base Case
if (index >= k):
return 0;
## Stores the minimum value
## of the current state
res = 1000
## If dp[state][index] is
## already calculated
if (dp[state][index] != -1):
## return dp[state][index]
return dp[state][index];
## Traverse over all the bits
for bit in bits:
## If count of set bit is N/K
if (__builtin_popcount(bit) == goal):
## Store the new state after
## choosing N/K elements
newstate = state;
## Stores the minimum and
## maximum of current subset
mn = 100
mx = -1
## Stores if the elements
## have been already
## selected or not
visit = [False]*(n+1)
## Stores if it is possible
## to select N/K elements
good = True
## Traverse over the array
for j in range(0, n):
## If not chosen already
## for another subset
if ((bit & (1 << j)) != 0):
## If already chosen
## for another subset
## or current subset
if (visit[A[j]] == True or (state & (1 << j)) == 0):
## Mark the good false
good = False;
break;
## Mark the current
## number visited
visit[A[j]] = True;
## Mark the current
## position in mask
## newstate
newstate = newstate ^ (1 << j);
## Update the maximum
## and minimum
mx = max(mx, A[j])
mn = min(mn, A[j])
## If good is True then
## Update the res
if (good):
res = min(res, mx - mn + dfs(A, newstate, index + 1))
## Update the current sp state
dp[state][index] = res
## Return the res
return res
## Function to find the minimum
## incompatibility sum
def minimumIncompatibility(A, K):
global k
global n
global goal
global dp
global bits
n = len(A)
k = K
goal = n // k
## Stores the count of element
mp = {}
## Traverse the array
for i in A:
## If number i not occurs
## in Map
if (i not in mp):
## Put the element
## in the Map
mp[i] = 0
## Increment the count of
## i in the Hash Map
mp[i]+=1
## If count of i in Map is
## greater than K then
## return -1
if (mp[i] > k):
return -1;
## Stores all total state
state = (1 << n) - 1;
## Traverse over all the state
for i in range(state + 1):
## If number of set bit
## is equal to N/K
if (__builtin_popcount(i) == goal):
bits.append(i)
## Stores the minimum value
## at a state
for i in range(1 << n):
dp.append([])
for j in range(k):
dp[i].append(-1)
## Call the recursive function
return dfs(A, state, 0);
## Driver code
if __name__=='__main__':
arr = [ 1, 2, 1, 4 ]
K = 2
## Function Call
print(minimumIncompatibility(arr, K))
# This code is contributed by subhamgoyal2014.
// C# program for the above approach
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
class Solution {
int k;
int n;
int goal;
int [,]dp;
List<int> bits = new List<int>();
// Function to find the minimum
// incompatibility sum
public int minimumIncompatibility(
int[] A, int k)
{
this.n = A.Length;
this.k = k;
goal = n / k;
// Stores the count of element
Dictionary<int,int> map
= new Dictionary<int,int>();
// Traverse the array
foreach(int i in A) {
// If number i not occurs
// in Map
if (!map.ContainsKey(i))
// Put the element
// in the Map
map[i]= 0;
// Increment the count of
// i in the Hash Map
map[i]++;
// If count of i in Map is
// greater than K then
// return -1
if (map[i] > k)
return -1;
}
// Stores all total state
int state = (1 << n) - 1;
// Traverse over all the state
for (int i = 0; i <= state; i++) {
// If number of set bit
// is equal to N/K
if (Convert.ToString(i, 2).Count(c => c == '1') == goal)
bits.Add(i);
}
// Stores the minimum value
// at a state
dp = new int[1 << n,k];
// Initialize the dp state
// with -1
for (int i = 0;i < dp.GetLength(0); i++) {
for (int j = 0;j < dp.GetLength(1); j++) {
dp[i,j]=-1;
}
}
// Call the recursive function
return dfs(A, state, 0);
}
// Recursive function to find
// the minimum required value
public int dfs(int []A, int state,
int index)
{
// Base Case
if (index >= k) {
return 0;
}
// Stores the minimum value
// of the current state
int res = 1000;
// If dp[state][index] is
// already calculated
if (dp[state,index] != -1) {
// return dp[state][index]
return dp[state,index];
}
// Traverse over all the bits
foreach(int bit in bits) {
// If count of set bit is N/K
if(Convert.ToString(bit, 2).Count(c => c == '1')== goal) {
// Store the new state after
// choosing N/K elements
int newstate = state;
// Stores the minimum and
// maximum of current subset
int mn = 100, mx = -1;
// Stores if the elements
// have been already
// selected or not
bool []visit
= new bool[n + 1];
// Stores if it is possible
// to select N/K elements
bool good = true;
// Traverse over the array
for (int j = 0; j < n; j++) {
// If not chosen already
// for another subset
if ((bit & (1 << j)) != 0) {
// If already chosen
// for another subset
// or current subset
if (visit[A[j]] == true
|| (state & (1 << j)) == 0) {
// Mark the good false
good = false;
break;
}
// Mark the current
// number visited
visit[A[j]] = true;
// Mark the current
// position in mask
// newstate
newstate = newstate
^ (1 << j);
// Update the maximum
// and minimum
mx = Math.Max(mx,
A[j]);
mn = Math.Min(mn,
A[j]);
}
}
// If good is true then
// Update the res
if (good) {
res = Math.Min(
res, mx - mn
+ dfs(A, newstate,
index + 1));
}
}
}
// Update the current sp state
dp[state,index] = res;
// Return the res
return res;
}
}
// Driver Code
class GFG {
public static void Main()
{
Solution st = new Solution();
int[] arr = { 1, 2, 1, 4 };
int K = 2;
// Function Call
Console.Write(
st.minimumIncompatibility(arr, K));
}
}
// This code is contributed by rutvik_56.
<script>
// Javascript program for the above approach
let k,n,goal;
let dp;
let bits = [];
// Function to find the minimum
// incompatibility sum
function minimumIncompatibility(A,K)
{
n = A.length;
k = K;
goal = Math.floor(n / k);
// Stores the count of element
let map = new Map();
// Traverse the array
for (let i=0;i< A.length;i++) {
// If number i not occurs
// in Map
if (!map.has(A[i]))
// Put the element
// in the Map
map.set(A[i], 0);
// Increment the count of
// i in the Hash Map
map.set(A[i], map.get(A[i]) + 1);
// If count of i in Map is
// greater than K then
// return -1
if (map.get(A[i]) > k)
return -1;
}
// Stores all total state
let state = (1 << n) - 1;
// Traverse over all the state
for (let i = 0; i <= state; i++) {
// If number of set bit
// is equal to N/K
if (i.toString(2).split('0').join('').length == goal)
bits.push(i);
}
// Stores the minimum value
// at a state
dp = new Array(1 << n);
// Initialize the dp state
// with -1
for (let i = 0;
i < dp.length; i++) {
dp[i]=new Array(k);
for(let j=0;j<k;j++)
dp[i][j]=-1;
}
// Call the recursive function
return dfs(A, state, 0);
}
// Recursive function to find
// the minimum required value
function dfs(A,state,index)
{
// Base Case
if (index >= k) {
return 0;
}
// Stores the minimum value
// of the current state
let res = 1000;
// If dp[state][index] is
// already calculated
if (dp[state][index] != -1) {
// return dp[state][index]
return dp[state][index];
}
// Traverse over all the bits
for (let bit=0;bit< bits.length;bit++) {
// If count of set bit is N/K
if (bits[bit].toString(2).split('0').join('').length
== goal) {
// Store the new state after
// choosing N/K elements
let newstate = state;
// Stores the minimum and
// maximum of current subset
let mn = 100, mx = -1;
// Stores if the elements
// have been already
// selected or not
let visit
= new Array(n + 1);
for(let i=0;i<=n;i++)
{
visit[i]=false;
}
// Stores if it is possible
// to select N/K elements
let good = true;
// Traverse over the array
for (let j = 0; j < n; j++) {
// If not chosen already
// for another subset
if ((bits[bit] & (1 << j)) != 0) {
// If already chosen
// for another subset
// or current subset
if (visit[A[j]] == true
|| (state & (1 << j)) == 0) {
// Mark the good false
good = false;
break;
}
// Mark the current
// number visited
visit[A[j]] = true;
// Mark the current
// position in mask
// newstate
newstate = newstate
^ (1 << j);
// Update the maximum
// and minimum
mx = Math.max(mx,
A[j]);
mn = Math.min(mn,
A[j]);
}
}
// If good is true then
// Update the res
if (good) {
res = Math.min(
res, mx - mn
+ dfs(A, newstate,
index + 1));
}
}
}
// Update the current sp state
dp[state][index] = res;
// Return the res
return res;
}
// Driver Code
let arr=[1, 2, 1, 4];
let K = 2;
document.write(minimumIncompatibility(arr, K))
// This code is contributed by unknown2108
</script>
Output:
4
Time Complexity: O(N2*22*N)
Auxiliary Space: O(N)