Reputation: 396
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?
Upvotes: 6
Views: 6844
Reputation: 41
def subSetSumK(ind, sm, arr, dp):
if ind == len(arr):
return int(sm == 0)
if (ind, sm) in dp:
return dp[(ind, sm)]
# skip the element
res = subSetSumK(ind + 1, sm, arr, dp)
# take it if it fall with in range
# if sm >= arr[ind]:
res += subSetSumK(ind + 1, sm - arr[ind], arr, dp)
dp[(ind, sm)] = res
return res % mod
def perfectSum(arr, sm):
# code here
n = len(arr)
dp = {}
return subSetSumK(0, sm, arr, dp)
Upvotes: 0
Reputation: 1450
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.
Upvotes: 2
Reputation: 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.
Upvotes: -1
Reputation: 1434
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);
}
}
Upvotes: -1