How to divide a set into two subsets such that difference between the sum of numbers in two sets is minimal?

前端 未结 18 649
攒了一身酷
攒了一身酷 2020-11-27 10:55

Given a set of numbers, divide the numbers into two subsets such that difference between the sum of numbers in two subsets is minimal.

T

相关标签:
18条回答
  • 2020-11-27 11:47

    Are you sorting your subset into decending order or ascending order?

    Think about it like this, the array {1, 3, 5, 8, 9, 25}

    if you were to divide, you would have {1,8,9} =18 {3,5,25} =33

    If it were sorted into descending order it would work out a lot better

    {25,1}=26 {9,8,5,3}=25

    So your solution is basically correct, it just needs to make sure to take the largest values first.

    EDIT: Read tskuzzy's post. Mine does not work

    0 讨论(0)
  • 2020-11-27 11:50

    No, Your algorithm is wrong. Your algo follows a greedy approach. I implemented your approach and it failed over this test case: (You may try here)

    A greedy algorithm:

    #include<bits/stdc++.h>
    #define rep(i,_n) for(int i=0;i<_n;i++)
    using namespace std;
    
    #define MXN 55
    int a[MXN];
    
    int main() {
        //code
        int t,n,c;
        cin>>t;
        while(t--){
            cin>>n;
            rep(i,n) cin>>a[i];
            sort(a, a+n);
            reverse(a, a+n);
            ll sum1 = 0, sum2 = 0;
            rep(i,n){
                cout<<a[i]<<endl;
                if(sum1<=sum2) 
                    sum1 += a[i]; 
                else 
                    sum2 += a[i]; 
            }
            cout<<abs(sum1-sum2)<<endl;
        }
        return 0;
    }
    

    Test case:

    1
    8 
    16 14 13 13 12 10 9 3
    
    Wrong Ans: 6
    16 13 10 9
    14 13 12 3
    
    Correct Ans: 0
    16 13 13 3
    14 12 10 9
    

    The reason greedy algorithm fails is that it does not consider cases when taking a larger element in current larger sum set and later a much smaller in the larger sum set may result much better results. It always try to minimize current difference without exploring or knowing further possibilities, while in a correct solution you might include an element in a larger set and include a much smaller element later to compensate this difference, same as in above test case.

    Correct Solution:

    To understand the solution, you will need to understand all below problems in order:

    • 0/1 Knapsack with Dynamic Programming
    • Partition Equal Subset Sum with DP
    • Solution

    My Code (Same logic as this):

    #include<bits/stdc++.h>
    #define rep(i,_n) for(int i=0;i<_n;i++)
    using namespace std;
    
    #define MXN 55
    int arr[MXN];
    int dp[MXN][MXN*MXN];
    
    int main() {
        //code
        int t,N,c;
        cin>>t;
        while(t--){
            rep(i,MXN) fill(dp[i], dp[i]+MXN*MXN, 0);
    
            cin>>N;
            rep(i,N) cin>>arr[i];
            int sum = accumulate(arr, arr+N, 0);
            dp[0][0] = 1;
            for(int i=1; i<=N; i++)
                for(int j=sum; j>=0; j--)
                    dp[i][j] |= (dp[i-1][j] | (j>=arr[i-1] ? dp[i-1][j-arr[i-1]] : 0));
    
            int res = sum;
    
            for(int i=0; i<=sum/2; i++)
                if(dp[N][i]) res = min(res, abs(i - (sum-i)));
    
            cout<<res<<endl;
        }
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 11:50
    I'll convert this problem to subset sum problem
    let's  take array int[] A = { 10,20,15,5,25,33 };
    it should be divided into {25 20 10} and { 33 20 } and answer is 55-53=2
    
    Notations : SUM == sum of whole array
                sum1 == sum of subset1
                sum2 == sum of subset1
    
    step 1: get sum of whole array  SUM=108
    step 2:  whichever way we divide our array into two part one thing will remain true
              sum1+ sum2= SUM
    step 3: if our intention is to get minimum sum difference then sum1 and sum2 should be near SUM/2 (example sum1=54 and sum2=54 then diff=0 )
    
    steon 4: let's try combinations
    
    
            sum1 = 54 AND sum2 = 54   (not possible to divide like this) 
            sum1 = 55 AND sum2 = 53   (possible and our solution, should break here)
            sum1 = 56 AND sum2 = 52  
            sum1 = 57 AND sum2 = 51 .......so on
            pseudo code
            SUM=Array.sum();
            sum1 = SUM/2;
            sum2 = SUM-sum1;
            while(true){
              if(subSetSuMProblem(A,sum1) && subSetSuMProblem(A,sum2){
               print "possible"
               break;
              }
             else{
              sum1++;
              sum2--;
             }
             }
    

    Java code for the same

    import java.util.ArrayList;
    import java.util.List;
    
    public class MinimumSumSubsetPrint {
    
    
    public static void main(String[] args) {
        int[] A = {10, 20, 15, 5, 25, 32};
        int sum = 0;
        for (int i = 0; i < A.length; i++) {
            sum += A[i];
        }
        subsetSumDynamic(A, sum);
    
    }
    
    private static boolean subsetSumDynamic(int[] A, int sum) {
        int n = A.length;
        boolean[][] T = new boolean[n + 1][sum + 1];
    
    
        // sum2[0][0]=true;
    
        for (int i = 0; i <= n; i++) {
            T[i][0] = true;
        }
    
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= sum; j++) {
                if (A[i - 1] > j) {
                    T[i][j] = T[i - 1][j];
                } else {
                    T[i][j] = T[i - 1][j] || T[i - 1][j - A[i - 1]];
                }
            }
        }
    
        int sum1 = sum / 2;
        int sum2 = sum - sum1;
        while (true) {
            if (T[n][sum1] && T[n][sum2]) {
                printSubsets(T, sum1, n, A);
                printSubsets(T, sum2, n, A);
                break;
            } else {
                sum1 = sum1 - 1;
                sum2 = sum - sum1;
                System.out.println(sum1 + ":" + sum2);
            }
        }
    
    
        return T[n][sum];
    }
    
    private static void printSubsets(boolean[][] T, int sum, int n, int[] A) {
        List<Integer> sumvals = new ArrayList<Integer>();
        int i = n;
        int j = sum;
        while (i > 0 && j > 0) {
            if (T[i][j] == T[i - 1][j]) {
                i--;
            } else {
                sumvals.add(A[i - 1]);
    
                j = j - A[i - 1];
                i--;
    
            }
        }
    
    
        System.out.println();
        for (int p : sumvals) {
            System.out.print(p + " ");
        }
        System.out.println();
    }
    
    
    }
    
    0 讨论(0)
  • 2020-11-27 11:50
    int ModDiff(int a, int b)
    {
        if(a < b)return b - a;
        return a-b;
    }
    
    int EqDiv(int *a, int l, int *SumI, int *SumE)
    {
        static int tc = 0;
        int min = ModDiff(*SumI,*SumE);
        for(int i = 0; i < l; i++)
        {
                swap(a,0,i);
                a++;
                int m1 = EqDiv(a, l-1, SumI,SumE);
                a--;
                swap(a,0,i);
    
                *SumI = *SumI + a[i];
                *SumE = *SumE - a[i];
                swap(a,0,i);
                a++;
                int m2 = EqDiv(a,l-1, SumI,SumE);
                a--;
                swap(a,0,i);
                *SumI = *SumI - a[i];
                *SumE = *SumE + a[i];
    
                min = min3(min,m1,m2);
    
        }
        return min;
    

    }

    call the function with SumI =0 and SumE= sumof all the elements in a. This O(n!) solution does compute the way we can divide the given array into 2 parts such the difference is minimum. But definitely not practical due to the n! time complexity looking to improve this using DP.

    0 讨论(0)
  • 2020-11-27 11:51

    One small change: reverse the order - start with the largest number and work down. This will minimize the error.

    0 讨论(0)
  • 2020-11-27 11:53

    Many answers mentioned about getting an 'approximate' solution in a very acceptable time bound . But since it is asked in an interview , I dont expect they need an approximation algorithm. Also I dont expect they need a naive exponential algorithm either.

    Coming to the problem , assuming the maximum value of sum of numbers is known , it can infact be solved in polynomial time using dynamic programming. Refer this link https://people.cs.clemson.edu/~bcdean/dp_practice/dp_4.swf

    0 讨论(0)
提交回复
热议问题