5

Given an array of integers and a sum, the task is to print all subsets of given array with sum equal to given sum.

Example: 
Input : arr[] = {1, 2, 3, 4, 5}
        sum = 10
Output : [4 3 2 1]  
         [5 3 2] 
         [5 4 1]

Input : arr[] = {-1, 2, 3, 4, 5}
        sum = 10
Output : [5 3 2] 
         [5 4 2 -1]

I have done that using dynamic programming in pseudo polynomial time. This is an extension of subset sum problem, which only takes care of deciding whether such a subset exist or not. My solution below works for both positive and negative numbers for the subset sum problem. However, it is not able to print the subsets correctly if the array contains negative numbers.The program is-

import java.util.ArrayList;

// sum problem
class GFG {

    static boolean subset[][];

    // Returns true if there is a subset of
    // set[] with sun equal to given sum
    static boolean isSubsetSum(int set[],
                               int n, int sum) {
        // The value of subset[i][j] will be
        // true if there is a subset of
        // set[0..j-1] with sum equal to i
        subset = new boolean[n + 1][sum + 1];

        // Fill the subset table in botton
        // up manner
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {
                if (j == 0) {
                    subset[i][j] = true;
                } else if (i <= 0 && sum >= 1)
                    subset[i][j] = false;
                else if (set[i - 1] > j)
                    subset[i][j] = subset[i - 1][j];
                else {
                    if (set[i - 1] >= 0)
                        subset[i][j] = subset[i - 1][j] || subset[i - 1][j - set[i - 1]];
                    else
                        subset[i][j] = subset[i - 1][j] || subset[i - 1][j + set[i - 1]];
                }
            }
        }

        // uncomment this code to print table
//        for (int i = 0; i <= sum; i++)
//        {
//        for (int j = 0; j <= n; j++)
//            System.out.println (subset[i][j]);
//        }

        return subset[n][sum];
    }

    /* Driver program to test above function */
    public static void main(String args[]) {
        int set[] = {1, 2, 3, 4, 5};
        int sum = 10;
        int n = set.length;
        if (isSubsetSum(set, n, sum) == true)
            System.out.println("Found a subset"
                    + " with given sum");
        else
            System.out.println("No subset with"
                    + " given sum");
        System.out.println("Done");
        ArrayList<Integer> list = new ArrayList<>();
        printSubsets(set, n, sum, list);
        System.out.println("Finished");
    }

    static void display(ArrayList<Integer> v) {
        System.out.println(v);
    }

    private static void printSubsets(int[] set, int i, int sum, ArrayList<Integer> list) {
        if (i == 0 && sum != 0 && subset[0][sum]) {
            list.add(set[i]);
            display(list);
            list.clear();
            return;
        }

        // If sum becomes 0
        if (i == 0 && sum == 0) {
            display(list);
            list.clear();
            return;
        }

        // If given sum can be achieved after ignoring
        // current element.
        if (subset[i - 1][sum]) {
            // Create a new vector to store path
            ArrayList<Integer> b = new ArrayList<>();
            b.addAll(list);
            printSubsets(set, i - 1, sum, b);
        }

        // If given sum can be achieved after considering
        // current element.

        if (sum >= set[i - 1] && subset[i - 1][sum - set[i - 1]]) {
            list.add(set[i - 1]);
            printSubsets(set, i - 1, sum - set[i - 1], list);
        }

    }   
} 

How this code can be modified to work for negative numbers as well?

Somesh Dhal
  • 336
  • 2
  • 15

3 Answers3

1

Your solutions assumes that all values are positive, so the dynamic programing array subset is filled with the values of j that are positive, but you need to take into account negative sums now.

What you need to do is to change the loop limits of j to fill the dynamic programing array to

for (int j = negative_sum; j <= positive_sum; j++)

Where negative_sum is the sum of all the negative values and positive_sum is the sum of all the positive ones.

For more details read the wikipedia page for the Subset Sum Problem here where this step is explained.

Rolando Urquiza
  • 1,404
  • 3
  • 17
  • 29
-1

Since you have to print ( or generate ) all possible subset of given set (containing both positive and negative integers) which have summation equal to sum, what you can do is :

try to represent each position of set as binary representation of 0 and 1, where 1 indicates that element at that position is taken and 0 indicates that element at that position is not taken into account.

Find the summation of all positions where there is 1. If the summation of these values is exactly equals to the given sum, then print that subset.

So, overall time complexity is O(2 ^ n), where n is length of given set.

You may look at following implementation.

import java.util.Arrays;

public class PerfectSum {

public static void printSubsets(int[] set, int n, int sum) {
     int totalSubSets = (1 << n);
     for (int i = 1; i < totalSubSets; ++i) { // loop over all possible subsets
         int curSum = 0;
         for (int j = n - 1; j >= 0; --j) {
             if (((i >> j) & 1) > 0) { // if bit at jth position is 1 take that value
                curSum +=set[j];
             }
         }
         if (curSum == sum) { // valid subset found, then print it
             for (int j = n - 1; j >= 0; --j) { // looping in reverse order to print set in decreasing order
                 if (((i >> j) & 1) > 0) { // if bit at jth position is 1 take that value
                     System.out.print(set[j] + " ");
                 }
             }
             System.out.println("");
         }
     }
}

public static void main(String[] args) {
    int set[] = {-1, 2, 3, 4, 5};
    Arrays.sort(set); // To print in non increasing order
    int sum = 10;
    int n = set.length;
    printSubsets(set, n, sum);
  }
}
BishalG
  • 1,414
  • 13
  • 24
  • Thanks for the solution . But I think this is kind of brute force where we iterate through all of them to get the proper subsets. Is there any way we can do it using dynamic programming(i.e in pseudo polynomial time )? – Somesh Dhal Sep 19 '18 at 18:23
  • Since, we have to print out all possible subsets, we can not do it in less than O(2 ^ n). Thanks !! – BishalG Sep 20 '18 at 06:51
-1

You can subtract the minimum negative number of the array to the entire set, making the numbers in the array positive. Then apply dynamic programming.