Minimum sum partition of an array

。_饼干妹妹 提交于 2020-01-13 05:50:36

问题


Problem Statement:

Given an array, the task is to divide it into two sets S1 and S2 such that the absolute difference between their sums is minimum.

Sample Inputs,

[1,6,5,11] => 1. The 2 subsets are {1,5,6} and {11} with sums being 12 and 11. Hence answer is 1.

[36,7,46,40] => 23. The 2 subsets are {7,46} and {36,40} with sums being 53 and 76. Hence answer is 23.

Constraints

1 <= size of array <= 50

1 <= a[i] <= 50

My Effort:

int someFunction(int n, int *arr) {
    qsort(arr, n, sizeof(int), compare);// sorted it for simplicity
    int i, j;
    int dp[55][3000]; // sum of the array won't go beyond 3000 and size of array is less than or equal to 50(for the rows) 

    // initialize
    for (i = 0; i < 55; ++i) {
        for (j = 0; j < 3000; ++j)
            dp[i][j] = 0;
    }

    int sum = 0;
    for (i = 0; i < n; ++i)
        sum += arr[i];

    for (i = 0; i < n; ++i) {
        for (j = 0; j <= sum; ++j) {
            dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
            if (j >= arr[i])
                dp[i + 1][j + 1] = max(dp[i + 1][j + 1], arr[i] + dp[i][j + 1 - arr[i]]);
        }
    }

    for (i = 0; i < n; ++i) {
        for (j = 0; j <= sum; ++j)
            printf("%d ", dp[i + 1][j + 1]);
        printf("\n");
    }
    return 0;// irrelevant for now as I am yet to understand what to do next to get the minimum. 
}

OUTPUT

Let's say for input [1,5,6,11], I am getting the dp array output as below.

0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
0 1 1 1 1 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 
0 1 1 1 1 5 6 7 7 7 7 11 12 12 12 12 12 12 12 12 12 12 12 12 
0 1 1 1 1 5 6 7 7 7 7 11 12 12 12 12 16 17 18 18 18 18 22 23 

Now, how to decide the 2 subsets to get the minimum?

P.S - I have already seen this link but explanation is not good enough for a DP beginner like me.


回答1:


You have to solve subset sum problem for SumValue = OverallSum / 2

Note that you don't need to solve any optimization problem (as using max operation in your code reveals).

Just fill linear table (1D array A) of size (SumValue + 1) with possible sums, get the closest to the last cell non-zero result (scan A backward) wint index M and calculate final result as abs(OverallSum - M - M).

To start, set 0-th entry to 1. Then for every source array item D[i] scan array A from the end to beginning:

A[0] = 1;
for (i = 0; i < D.Length(); i++)
  {
    for (j = SumValue; j >= D[i]; j--)  
      {
        if (A[j - D[i]] == 1)   
       // we can compose sum j from D[i] and previously made sum
             A[j] = 1;
       }
   }  

For example D = [1,6,5,11] you have SumValue = 12, make array A[13], and calculate possible sums

A array after filling: [0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1]

working Python code:

def besthalf(d):
    s = sum(d)
    half = s // 2
    a = [1] + [0] * half
    for v in d:
        for j in range(half, v - 1, -1):
            if (a[j -v] == 1):
                a[j] = 1
    for j in range(half, 0, -1):
        if (a[j] == 1):
            m = j
            break
    return(s - 2 * m)

print(besthalf([1,5,6,11]))
print(besthalf([1,1,1,50]))
>>1
>>47



回答2:


Working C code if anyone is interested but the idea remains the same as what @MBo said

int someFunction(int n,int *arr)
{
    qsort (arr, n, sizeof(int), compare);
    int i,j;
    int dp[3000];

    for(j=0;j<3000;++j) dp[j] = 0;

    int sum = 0;
    for(i=0;i<n;++i) sum += arr[i];

    int sum2 = sum;

    if((sum&1) == 1) sum = sum/2+1;
    else sum = sum/2;

    dp[0] = 1;
    for(i=0;i<n;++i){
        for(j=sum;j>=arr[i];--j){
           if(dp[j-arr[i]] == 1){
               dp[j] = 1;
           }       
        }
     }

    for(i=sum;i>=1;--i){
        if(dp[i] == 1){
            return abs(sum2 - 2 * i);
        }
    }


    return 0;
}


来源:https://stackoverflow.com/questions/51711387/minimum-sum-partition-of-an-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!