C语言 数组的最小和划分

k2fxgqgv  于 2022-12-03  发布在  其他
关注(0)|答案(3)|浏览(147)

问题陈述:

给定一个数组,任务是将其分成两个集合S1和S2,使得它们的和之间的绝对差最小。

样本输入

[1,6,5,11] =〉1。两个子集是{1,5,6}{11},和是1211。因此答案是1
[36,7,46,40] =〉23。两个子集是{7,46}{36,40},和是5376。因此答案是23

限制

1〈=数组大小〈= 50
1〈= a[i]〈= 50

我的努力:

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

输出

假设对于输入[1,5,6,11],我得到dp数组输出如下。

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

现在,如何决定这两个子集以获得最小值?
我已经看过这个link,但是对于像我这样的DP初学者来说,解释还不够好。

1zmg4dgp

1zmg4dgp1#

您必须为SumValue = OverallSum / 2解决subset sum问题
请注意,您不需要解决任何优化问题(正如在代码中使用max操作所揭示的那样)。
只需用可能的和填充大小为(SumValue + 1)的线性表(1D数组A),获得最接近最后一个单元格的非零结果(向后扫描A)wint索引M,并将最终结果计算为abs(OverallSum - M - M)
首先,将第0个条目设置为1。然后,对于每个源数组项D[i],从末尾到开头扫描数组A

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;
       }
   }

例如D = [1,6,5,11],您有SumValue = 12,使数组A[13],并计算可能的总和

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

工作Python代码:

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
bvhaajcl

bvhaajcl2#

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代码

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();
}

}
vq8itlhq

vq8itlhq3#

如果有人感兴趣的话,我会写一些Java代码,但想法和@MBo的回答一样

class Solution{
    public int minDifference(int arr[]) { 
        int sum = 0;
        for(int x : arr) sum += x;
        int half = (sum >> 1) + (sum & 1);
        boolean[] sums = new boolean[half + 1];
        sums[0] = true;
        for(int i = 0; i < arr.length; ++i){
            if(arr[i] > half) continue;
            for(int j = half; j >= arr[i]; --j){
                if(sums[j - arr[i]]){
                    sums[j] = true;
                }
            }
        }
        
        for(int i = sums.length - 1; i >= 1; --i){
            if(sums[i]) return Math.abs((sum - i) - i);
        }
        
        return sum; // for arrays like [2] or [100] etc
    } 
}

相关问题