Open In App

Minimum Subset sum difference problem with Subset partitioning

Given a set of N integers with up to 40 elements, the task is to partition the set into two subsets of equal size (or the closest possible), such that the difference between the sums of the subsets is minimized. If the size of the set is odd, one subset will have one more element than the other. If the size is even, both subsets will have the same size. After finding the minimum difference, output the subsets.

Examples:



Input: arr[] = {45, 34, 4, 12, 5, 2}
Output: Min Difference 0 Subset 1 : {34, 12,  5} Subset 2 : {45,  4,  2}                        
Explanation: The above generated two subsets have sum
? Subset 1 : 34+12+5 =  51
? Subset 2 : 45+4+2 =  51
The minimum difference is abs( ? S1 – ? S2 ) = 0, which is the minimum difference possible.

Input: arr[] = {3, 34, 4, 12, 5, 2 }
Output: Min Difference 18 Subset 1 : {3  34  2} Subset 2 : {4  12  5 }                
Explanation: The above generated two subsets have sum
? Subset 1 : 3+34+2 =  39
? Subset 2 : 4+12+5 =  21
The minimum difference is abs( ? S1 – ? S2 ) = 18, which is the minimum difference possible.



Approach: To solve the problem, follow the below idea:

The idea is to partition the array into two different subsets and then generate the subsets.

Below are the steps for the above approach:

Below is the implementation for the above approach:




#include <bits/stdc++.h>
using namespace std;
 
struct Info {
    int sum;
    vector<int> indices;
};
 
static bool cmp(Info& p1, Info& p2)
{
    return p1.sum < p2.sum;
}
 
void generate(vector<int>& arr, int curr, int n, int sum,
              vector<vector<Info> >& store,
              vector<int> build)
{
    if (curr == n) {
        int sz = build.size();
        store[sz].push_back({ sum, build });
        return;
    }
    build.push_back(curr);
    generate(arr, curr + 1, n, sum + arr[curr], store,
             build);
    build.pop_back();
    generate(arr, curr + 1, n, sum, store, build);
}
 
int BINRY_SRCH(vector<Info>& arr, int target)
{
 
    // Lower bound
    int res = -1;
    int low = 0;
    int high = arr.size() - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (arr[mid].sum >= target) {
            res = mid;
            high = mid - 1;
        }
        else {
            low = mid + 1;
        }
    }
    return res;
}
 
vector<vector<int> > minDifference(vector<int>& arr, int n)
{
    int extra = (n % 2 != 0);
 
    vector<vector<Info> > part1(n / 2 + 1 + extra);
    vector<vector<Info> > part2(n / 2 + 1);
 
    generate(arr, 0, n / 2 + extra, 0, part1, {});
    generate(arr, n / 2 + extra, n, 0, part2, {});
 
    for (auto& vec : part2) {
 
        // Sorting part2 to prepare
        // for binary search
        sort(vec.begin(), vec.end(), cmp);
    }
 
    vector<vector<int> > res(2);
 
    int diff = INT_MAX;
    int TS = accumulate(arr.begin(), arr.end(), 0);
 
    // Making subset1
    for (int ele = 1; ele <= n / 2 + extra; ele++) {
 
        // Taking only ele
        // elements from part1
        vector<Info> P1 = part1[ele];
 
        // Taking rest of the elements
        // for subset1 from part2
        vector<Info> P2 = part2[n / 2 + extra - ele];
 
        // Iterating for each sum in P1
        for (auto x : P1) {
 
            // P1sum -> subset1 sum
            // P2sum -> subset2 sum
 
            // For absolute minimisation,
            // each subset should be having
            // sum close to TS/2. If we
            // take x sum from Part1, then
            // remaining sum TS/2-x should
            // be taken from part2. We
            // want to get a sum closer to
            // this target. For this, do
            // binary search.
            int index = BINRY_SRCH(P2, TS / 2 - x.sum);
            if (index != -1) {
                int subset1_Sum = x.sum + P2[index].sum;
                int subset2_Sum = TS - subset1_Sum;
 
                if (abs(subset1_Sum - subset2_Sum) < diff) {
                    diff = abs(subset1_Sum - subset2_Sum);
 
                    // Storing the subset
                    vector<int> subset1 = x.indices;
                    for (auto c : P2[index].indices) {
                        subset1.push_back(c);
                    }
                    res[0] = subset1;
                }
            }
 
            if (index > 0) {
                index--;
                int subset1_Sum = x.sum + P2[index].sum;
                int subset2_Sum = TS - subset1_Sum;
 
                if (abs(subset1_Sum - subset2_Sum) < diff) {
                    diff = abs(subset1_Sum - subset2_Sum);
 
                    // Storing the subset
                    vector<int> subset1 = x.indices;
                    for (auto c : P2[index].indices) {
                        subset1.push_back(c);
                    }
                    res[0] = subset1;
                }
            }
        }
    }
 
    // Find subset2 after ignoring elements
    // of subset1 in arr
 
    vector<bool> vis(n, false);
 
    for (int i = 0; i < res[0].size(); i++) {
        vis[res[0][i]] = true;
        res[0][i] = arr[res[0][i]];
    }
 
    vector<int> subset2;
    for (int i = 0; i < n; i++) {
        if (vis[i] == false) {
            subset2.push_back(arr[i]);
        }
    }
    res[1] = subset2;
 
    cout << "Min Difference " << diff << endl;
 
    return res;
}
 
void PRINT(vector<vector<int> >& subsets)
{
    cout << "Subset 1 : ";
    for (auto x : subsets[0]) {
        cout << x << "  ";
    }
    cout << endl
         << "Subset 2 : ";
    for (auto x : subsets[1]) {
        cout << x << "  ";
    }
}
 
// Drivers code
int main()
{
    vector<int> arr;
    vector<vector<int> > res;
 
    arr = {45, 34, 4, 12, 5, 2 };
    res = minDifference(arr, arr.size());
    PRINT(res);
    return 0;
}
 
// Author:- RainX (Abhijit Roy, NIT AGARTALA)




import java.util.Arrays;
 
public class Main {
    public static void main(String[] args) {
        int[] arr = {34, 45, 12, 4, 5, 2};
        findMinSubsetSumDiff(arr);
    }
 
    public static void findMinSubsetSumDiff(int[] arr) {
        int totalSum = 0;
        for (int i : arr) {
            totalSum += i;
        }
 
        int n = arr.length;
        boolean[][] dp = new boolean[n + 1][totalSum + 1];
 
        for (int i = 0; i <= n; i++) {
            dp[i][0] = true;
        }
 
        for (int i = 1; i <= totalSum; i++) {
            dp[0][i] = false;
        }
 
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= totalSum; j++) {
                dp[i][j] = dp[i - 1][j];
                if (arr[i - 1] <= j) {
                    dp[i][j] |= dp[i - 1][j - arr[i - 1]];
                }
            }
        }
 
        int diff = Integer.MAX_VALUE;
        for (int j = totalSum / 2; j >= 0; j--) {
            if (dp[n][j]) {
                diff = totalSum - (2 * j);
                break;
            }
        }
 
        int subset1Sum = (totalSum + diff) / 2;
        int subset2Sum = totalSum - subset1Sum;
        int[] subset1 = new int[n];
        int[] subset2 = new int[n];
        int i_ = n;
        int j_ = subset1Sum;
        int index1 = 0, index2 = 0;
        while (j_ > 0 && i_ > 0) {
            if (dp[i_ - 1][j_] == false) {
                subset1[index1] = arr[i_ - 1];
                index1++;
                j_ -= arr[i_ - 1];
            } else {
                subset2[index2] = arr[i_ - 1];
                index2++;
            }
            i_--;
        }
 
        System.out.println("Min Difference: " + diff);
        System.out.print("Subset 1: ");
        for (int k = index1 - 1; k >= 0; k--) {
            System.out.print(subset1[k] + " ");
        }
        System.out.print("\nSubset 2: ");
        for (int k = index2 - 1; k >= 0; k--) {
            System.out.print(subset2[k] + " ");
        }
    }
}




def find_min_subset_sum_diff(arr):
    total_sum = sum(arr)
    n = len(arr)
    dp = [[False for _ in range(total_sum+1)] for _ in range(n+1)]
 
    for i in range(n+1):
        dp[i][0] = True
 
    for i in range(1, total_sum+1):
        dp[0][i] = False
 
    for i in range(1, n+1):
        for j in range(1, total_sum+1):
            dp[i][j] = dp[i-1][j]
            if arr[i-1] <= j:
                dp[i][j] |= dp[i-1][j-arr[i-1]]
 
    diff = float('inf')
    for j in range(total_sum//2, -1, -1):
        if dp[n][j]:
            diff = total_sum - (2*j)
            break
 
    subset1_sum = (total_sum + diff) // 2
    subset2_sum = total_sum - subset1_sum
    subset1 = []
    subset2 = []
    i = n
    j = subset1_sum
    while j > 0 and i > 0:
        if dp[i-1][j] == False:
            subset1.append(arr[i-1])
            j -= arr[i-1]
        else:
            subset2.append(arr[i-1])
        i -= 1
 
    print("Min Difference:", diff)
    print("Subset 1 :", *subset1)
    print("Subset 2 :", *subset2)
 
arr = [34, 45, 12, 4, 5, 2]
find_min_subset_sum_diff(arr)




using System;
 
public class Program
{
    public static void Main()
    {
        int[] arr = {34, 45, 12, 4, 5, 2};
        FindMinSubsetSumDiff(arr);
    }
 
    public static void FindMinSubsetSumDiff(int[] arr)
    {
        int totalSum = 0;
        foreach (int i in arr)
        {
            totalSum += i;
        }
 
        int n = arr.Length;
        bool[,] dp = new bool[n + 1, totalSum + 1];
 
        for (int i = 0; i <= n; i++)
        {
            dp[i, 0] = true;
        }
 
        for (int i = 1; i <= totalSum; i++)
        {
            dp[0, i] = false;
        }
 
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= totalSum; j++)
            {
                dp[i, j] = dp[i - 1, j];
                if (arr[i - 1] <= j)
                {
                    dp[i, j] |= dp[i - 1, j - arr[i - 1]];
                }
            }
        }
 
        int diff = int.MaxValue;
        for (int j = totalSum / 2; j >= 0; j--)
        {
            if (dp[n, j] == true)
            {
                diff = totalSum - (2 * j);
                break;
            }
        }
 
        int subset1Sum = (totalSum + diff) / 2;
        int subset2Sum = totalSum - subset1Sum;
        int[] subset1 = new int[n];
        int[] subset2 = new int[n];
        int i_ = n;
        int j_ = subset1Sum;
        int index1 = 0, index2 = 0;
        while (j_ > 0 && i_ > 0)
        {
            if (dp[i_ - 1, j_] == false)
            {
                subset1[index1] = arr[i_ - 1];
                index1++;
                j_ -= arr[i_ - 1];
            }
            else
            {
                subset2[index2] = arr[i_ - 1];
                index2++;
            }
            i_--;
        }
 
        Console.WriteLine("Min Difference: " + diff);
        Console.Write("Subset 1: ");
        for (int k = index1 - 1; k >= 0; k--)
        {
            Console.Write(subset1[k] + " ");
        }
        Console.Write("\nSubset 2: ");
        for (int k = index2 - 1; k >= 0; k--)
        {
            Console.Write(subset2[k] + " ");
        }
    }
}




function findMinSubsetSumDiff(arr) {
  let totalSum = arr.reduce((a, b) => a + b);
  let n = arr.length;
  let dp = Array.from(Array(n + 1), () => Array(totalSum + 1));
 
  for (let i = 0; i <= n; i++) {
    dp[i][0] = true;
  }
 
  for (let i = 1; i <= totalSum; i++) {
    dp[0][i] = false;
  }
 
  for (let i = 1; i <= n; i++) {
    for (let j = 1; j <= totalSum; j++) {
      dp[i][j] = dp[i - 1][j];
      if (arr[i - 1] <= j) {
        dp[i][j] |= dp[i - 1][j - arr[i - 1]];
      }
    }
  }
 
  let diff = Number.MAX_SAFE_INTEGER;
  for (let j = Math.floor(totalSum / 2); j >= 0; j--) {
    if (dp[n][j] == true) {
      diff = totalSum - (2 * j);
      break;
    }
  }
 
  let subset1Sum = (totalSum + diff) / 2;
  let subset2Sum = totalSum - subset1Sum;
  let subset1 = [];
  let subset2 = [];
  let i_ = n;
  let j_ = subset1Sum;
  let index1 = 0,
    index2 = 0;
 
  while (j_ > 0 && i_ > 0) {
    if (dp[i_ - 1][j_] == false) {
      subset1[index1] = arr[i_ - 1];
      index1++;
      j_ -= arr[i_ - 1];
    } else {
      subset2[index2] = arr[i_ - 1];
      index2++;
    }
    i_--;
  }
 
  console.log("Min Difference: " + diff);
  console.log("Subset 1: " + subset1.reverse().join(" "));
  console.log("Subset 2: " + subset2.reverse().join(" "));
}
 
let arr = [34, 45, 12, 4, 5, 2];
findMinSubsetSumDiff(arr);




<?php
 
function findMinSubsetSumDiff($arr) {
    $totalSum = array_sum($arr);
 
    $n = count($arr);
    $dp = array_fill(0, $n + 1, array_fill(0, $totalSum + 1, false));
 
    for ($i = 0; $i <= $n; $i++) {
        $dp[$i][0] = true;
    }
 
    for ($i = 1; $i <= $totalSum; $i++) {
        $dp[0][$i] = false;
    }
 
    for ($i = 1; $i <= $n; $i++) {
        for ($j = 1; $j <= $totalSum; $j++) {
            $dp[$i][$j] = $dp[$i - 1][$j];
            if ($arr[$i - 1] <= $j) {
                $dp[$i][$j] |= $dp[$i - 1][$j - $arr[$i - 1]];
            }
        }
    }
 
    $diff = PHP_INT_MAX;
    for ($j = $totalSum / 2; $j >= 0; $j--) {
        if ($dp[$n][$j]) {
            $diff = $totalSum - (2 * $j);
            break;
        }
    }
 
    $subset1Sum = ($totalSum + $diff) / 2;
    $subset2Sum = $totalSum - $subset1Sum;
    $subset1 = array();
    $subset2 = array();
    $i_ = $n;
    $j_ = $subset1Sum;
    $index1 = 0;
    $index2 = 0;
 
    while ($j_ > 0 && $i_ > 0) {
        if ($dp[$i_ - 1][$j_] == false) {
            $subset1[$index1] = $arr[$i_ - 1];
            $index1++;
            $j_ -= $arr[$i_ - 1];
        } else {
            $subset2[$index2] = $arr[$i_ - 1];
            $index2++;
        }
        $i_--;
    }
 
    echo "Min Difference: " . $diff . PHP_EOL;
    echo "Subset 1: ";
    for ($k = $index1 - 1; $k >= 0; $k--) {
        echo $subset1[$k] . " ";
    }
    echo PHP_EOL . "Subset 2: ";
    for ($k = $index2 - 1; $k >= 0; $k--) {
        echo $subset2[$k] . " ";
    }
}
 
$arr = array(34, 45, 12, 4, 5, 2);
findMinSubsetSumDiff($arr);
 
?>

Output
Min Difference 0
Subset 1 : 34  12  5  
Subset 2 : 45  4  2  



Time Complexity: O( 2n/2  * log(2n/2) *  n/2 ),  O(2n/2) is for generating two subsets, O(log(2n/2)) for binary searching for each element of part1 in part2, hence the extra n/2 factor.
Auxiliary Space: O(2n/2), 2n/2 space is required for storing the half subset.


Article Tags :