Open In App

Minimum Subset sum difference problem with Subset partitioning

Last Updated : 29 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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:

  • Partition the array arr into two equal parts, if n is odd, one element is extra in either of the subsets. Make a custom structure that stores the generated subset indices and their sum. For each part, generate the subsets indices and store their (sum, subset) against the number of elements in the subset. That is, arr[ ni ] ={ sum, subset } where ni is the number of elements in the subset. For convenience, assign part1 the extra element in case of an odd size of the array.
  • Sort the sums in the vector part2, so we can binary search later on this.
  • The objective is to create subset1. Subset2 can be obtained by removing all elements of subset1 from arr. Subset1 will contain n/2 elements, +1 in case n is odd.
  • Run a loop from x = 1 to n/2 (+1 in case n is odd). 
    • The idea is if we are picking x elements from part1, then we need to pick (n/2 (+1 in case n is odd) – x) elements from part2.
    • We already have stored the generated subset and its sum for ni elements. Iterate on the sums generated using x elements in part1. For each sum S in P1[x], we need a sum closest to (?arr)/2 – S in part2 using the remaining elements (n/2 (+1 in case n is odd) – x). This is where binary search can be leveraged to quickly find the closest sum in part2 for sums in part1. Once found the closest sum in Part2.
    • The sum of subset1 is ? S1 = S + part2 index, where part2 index is the index returned by binary search.
    • The sum of subset2 is ? S2 = TotalSum of the array – subset1 sum.
    • If minimum difference abs( ? S1 – ? S2 ) is found. Store the corresponding generated subsets indices from part1 and part2
  • Once subset1 is generated. Initialize a Boolean vector of size n and mark all the indices of subset1, also update the indices to their corresponding values in the subset1 vector.
  • Iterate the Boolean vector after marking the subset1 indices and store the values corresponding to the non-marked indices in the Boolean vector.
  • Print the minimum difference, subset1, and subset2.

Below is the implementation for the above approach:

C++




#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)


Java




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] + " ");
        }
    }
}


Python3




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)


C#




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] + " ");
        }
    }
}


Javascript




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




<?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.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads