如何将一个集合分成两个子集,以使两个集合中数字之和之间的差异最小?

Posted

技术标签:

【中文标题】如何将一个集合分成两个子集,以使两个集合中数字之和之间的差异最小?【英文标题】:How to divide a set into two subsets such that difference between the sum of numbers in two sets is minimal? 【发布时间】:2011-09-29 15:27:06 【问题描述】:

给定一组数字,将这些数字分成两个子集,使得两个子集中的数字之和之间的差异最小。

这是我的想法,但我不确定这是否是正确的解决方案:

    对数组进行排序 取前 2 个元素。将它们视为 2 个集合(每个集合有 1 个元素) 从数组中取出下一个元素。 决定这个元素应该放在哪个集合中(通过计算总和 => 它应该是最小值) 重复

这是正确的解决方案吗?我们能做得更好吗?

【问题讨论】:

这是一个答案,这非常接近,几乎是重复的。 ***.com/a/5898540/1214064 这个怎么样:当我们说两个具有最小差异的子集时,我们的意思是两个总和最接近的子集。我相信这可以 O(nlogn) 完成,以下是步骤: 1. 预期子集 sum = sum_of_all_elements/2 2. 对数组进行排序 3. 从最后一个开始并继续添加,直到总和小于或大于预期之前计算的子集总和。 4.现在从上面的步骤中取出两个子集,给出最小的差异 其他具有相同答案的问题是与给定目标总和可能最接近的子集总和是多少(在这种情况下,它将是数组中所有数字总和的一半)。这样,您只需返回从总和/2 向左迭代的任何可能的总和。这是使用动态编程的 O(n) 解决方案。希望这会有所帮助 【参考方案1】:

您描述的问题的decision 版本是NP-complete 问题,它被称为partition problem。有许多 approximations 在许多情况下提供了最佳或至少足够好的解决方案。

您描述的简单算法是游乐场孩子们挑选球队的一种方式。如果集合中的数字具有相似的数量级,则此 greedy algorithm 的性能非常好。

美国科学家 的文章The Easiest Hardest Problem 对这个问题进行了出色的分析。你应该通读一遍!

【讨论】:

我们如何表达上述问题的决策版本(不是等和版本)?。【参考方案2】:

不,这行不通。没有多项式时间解(除非 P=NP)。您能做的最好的就是查看所有不同的子集。看看subset sum problem。

考虑列表[0, 1, 5, 6]。当最佳答案实际上是 0, 1, 56 时,您将声明 0, 51, 6

【讨论】:

这不是真的。贪心解法将 6 放入集合 A,5 放入集合 B,1 放入集合 B,0 放入集合 A。这是最优的。 哪种贪婪解决方案?我相信 OP 提出的解决方案会像我描述的那样执行。无论如何,这是没有实际意义的,因为问题是 np-complete... 我指的是 OP 的解决方案。我认为我们在阅读他的解决方案时做出了不同的假设。在排序阶段,我假设他会按照这个特定问题的自然降序排序。你假设上升,我同意这会产生一个相当糟糕的解决方案。【参考方案3】:

不,你的算法是错误的。您的算法遵循贪婪的方法。 我实施了您的方法,但在这个测试用例中失败了: (你可以试试here)

贪心算法:

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

测试用例:

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

贪心算法失败的原因是它没有考虑在当前较大的和集中取较大元素的情况,后来在较大的和集中取小得多的元素可能会产生更好的结果。它总是在不探索或了解更多可能性的情况下尝试最小化当前差异,而在正确的解决方案中,您可能会在更大的集合中包含一个元素,然后再包含一个更小的元素来补偿这种差异,与上述测试用例相同。

正确的解决方案:

要了解解决方案,您需要按顺序了解以下所有问题:

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

我的代码(与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;

【讨论】:

【参考方案4】:

组合优于组合方法:

import itertools as it

def min_diff_sets(data):
    """
        Parameters:
        - `data`: input list.
        Return:
        - min diff between sum of numbers in two sets
    """

    if len(data) == 1:
        return data[0]
    s = sum(data)
    # `a` is list of all possible combinations of all possible lengths (from 1
    # to len(data) )
    a = []
    for i in range(1, len(data)):
        a.extend(list(it.combinations(data, i)))
    # `b` is list of all possible pairs (combinations) of all elements from `a`
    b = it.combinations(a, 2)
    # `c` is going to be final correct list of combinations.
    # Let's apply 2 filters:
    # 1. leave only pairs where: sum of all elements == sum(data)
    # 2. leave only pairs where: flat list from pairs == data
    c = filter(lambda x: sum(x[0])+sum(x[1])==s, b)
    c = filter(lambda x: sorted([i for sub in x for i in sub])==sorted(data), c)
    # `res` = [min_diff_between_sum_of_numbers_in_two_sets,
    #           ((set_1), (set_2))
    #         ]
    res = sorted([(abs(sum(i[0]) - sum(i[1])), i) for i in c],
            key=lambda x: x[0])
    return min([i[0] for i in res])

if __name__ == '__main__':
    assert min_diff_sets([10, 10]) == 0, "1st example"
    assert min_diff_sets([10]) == 10, "2nd example"
    assert min_diff_sets([5, 8, 13, 27, 14]) == 3, "3rd example"
    assert min_diff_sets([5, 5, 6, 5]) == 1, "4th example"
    assert min_diff_sets([12, 30, 30, 32, 42, 49]) == 9, "5th example"
    assert min_diff_sets([1, 1, 1, 3]) == 0, "6th example"

【讨论】:

【参考方案5】:

递归方法是从数组的所有值中生成所有可能的和并检查 哪个解决方案是最优化的。 为了生成总和,我们要么将第 i 个项目包含在集合 1 中,要么不包含,即包含在 设置 2。

时间和空间的时间复杂度都是O(n*sum)。T

public class MinimumSubsetSum 

  static int dp[][];
  public static int minDiffSubsets(int arr[], int i, int calculatedSum, int totalSum) 

    if(dp[i][calculatedSum] != -1) return dp[i][calculatedSum];

    /**
     * If i=0, then the sum of one subset has been calculated as we have reached the last
     * element. The sum of another subset is totalSum - calculated sum. We need to return the
     * difference between them.
     */
    if(i == 0) 
      return Math.abs((totalSum - calculatedSum) - calculatedSum);
    

    //Including the ith element
    int iElementIncluded = minDiffSubsets(arr, i-1, arr[i-1] + calculatedSum,
        totalSum);

    //Excluding the ith element
    int iElementExcluded = minDiffSubsets(arr, i-1, calculatedSum, totalSum);

    int res = Math.min(iElementIncluded, iElementExcluded);
    dp[i][calculatedSum] = res;
    return res;
  

  public static void util(int arr[]) 
    int totalSum = 0;
    int n = arr.length;
    for(Integer e : arr) totalSum += e;
    dp = new int[n+1][totalSum+1];
    for(int i=0; i <= n; i++)
      for(int j=0; j <= totalSum; j++)
        dp[i][j] = -1;

    int res = minDiffSubsets(arr, n, 0, totalSum);
    System.out.println("The min difference between two subset is " + res);
  


  public static void main(String[] args) 
    util(new int[]3, 1, 4, 2, 2, 1);
  


【讨论】:

【参考方案6】:

我们可以使用动态规划(类似于我们发现一个集合是否可以划分为两个相等的子集的方法)。然后我们找到最大可能的总和,这将是我们的第一个分区。 第二个分区将是总和与 firstSum 的差。 答案将是第一和第二分区的差异。

public int minDiffernce(int set[])       
    int sum = 0;
    int n = set.length;
    for(int i=0; i<n; i++)
        sum+=set[i];

    //finding half of total sum, because min difference can be at max 0, if one subset reaches half
    int target = sum/2;
    boolean[][] dp = new boolean[n+1][target+1];//2

    for(int i = 0; i<=n; i++)
        dp[i][0] = true;
    for(int i= 1; i<=n; i++)
        for(int j = 1; j<=target;j++)
            if(set[i-1]>j) dp[i][j] = dp[i-1][j];
            else dp[i][j] = dp[i-1][j] || dp[i-1][j-set[i-1]];
        
    

    // we now find the max sum possible starting from target
    int firstPart = 0;
    for(int j = target; j>=0; j--)
        if(dp[n][j] == true) 
            firstPart = j; break;
        
    

    int secondPart = sum - firstPart;
    return Math.abs(firstPart - secondPart);

【讨论】:

【参考方案7】:

一个小的变化:颠倒顺序 - 从最大的数字开始,然后向下工作。这将最大限度地减少错误。

【讨论】:

这不起作用。考虑 [4,14,15,16,17]。当最佳答案是 17,16 和 15,14,4 时,您将声明 17,14,4 和 16,15【参考方案8】:

您是按降序还是升序对子集进行排序?

这样想,数组 1, 3, 5, 8, 9, 25

如果你要除法,你会得到 1,8,9 =18 3,5,25 =33

如果按降序排序,效果会好很多

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

所以你的解决方案基本上是正确的,它只需要确保首先取最大值。

编辑:阅读 tskuzzy 的帖子。我的不行

【讨论】:

谢谢您的回答...如果数组中有负数怎么办? 这不起作用。考虑 [4,14,15,16,17]。当最佳答案是 17,16 和 15,14,4 时,您将声明 17,14,4 和 16,15【参考方案9】:

这是背包和子集和问题的变体。 在子集和问题中,给定n个正整数和一个值k,我们必须找到值小于或等于k的子集的和。 在上面的问题中我们给了一个数组,这里我们必须找到总和小于或等于total_sum(数组值的总和)的子集。 所以 可以使用背包算法的变体找到子集和,通过 将利润作为给定的数组值。最后的答案是 total_sum-dp[n][total_sum/2]。看看下面的代码清楚 理解。

#include<iostream>
#include<cstdio>
using namespace std;
int main()

        int n;
        cin>>n;
        int arr[n],sum=0;
        for(int i=1;i<=n;i++)
        cin>>arr[i],sum+=arr[i];
        int temp=sum/2;
        int dp[n+1][temp+2];
        for(int i=0;i<=n;i++)
        
            for(int j=0;j<=temp;j++)
            
                if(i==0 || j==0)
                dp[i][j]=0;
                else if(arr[i]<=j)
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-arr[i]]+arr[i]);
                else
                
                dp[i][j]=dp[i-1][j];
                
            
        
        cout<<sum-2*dp[n][temp]<<endl;

【讨论】:

【参考方案10】:

这可以使用 BST 解决。 首先对数组进行排序说 arr1 使用 arr1 的最后一个元素开始创建另一个 arr2(从 arr1 中删除此 ele)

现在:重复这些步骤,直到没有交换发生。

    检查 arr1 是否有可以使用 BST 移动到 arr2 的元素,以使 diff 小于目前找到的 MIN diff。 如果我们找到一个元素,则将该元素移动到 arr2 并再次转到 step1。 如果我们在上述步骤中没有找到任何元素,请针对 arr2 和 arr1 执行步骤 1 和 2。 即现在检查 arr2 中是否有可以移动到 arr1 的元素 继续步骤 1-4,直到我们不需要任何交换为止.. 我们找到了解决方案。

示例 Java 代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Divide an array so that the difference between these 2 is min
 * 
 * @author shaikhjamir
 *
 */
public class DivideArrayForMinDiff 

    /**
     * Create 2 arrays and try to find the element from 2nd one so that diff is
     * min than the current one
     */

    private static int sum(List<Integer> arr) 

        int total = 0;
        for (int i = 0; i < arr.size(); i++) 
            total += arr.get(i);
        

        return total;
    

    private static int diff(ArrayList<Integer> arr, ArrayList<Integer> arr2) 
        int diff = sum(arr) - sum(arr2);
        if (diff < 0)
            diff = diff * -1;
        return diff;
    

    private static int MIN = Integer.MAX_VALUE;

    private static int binarySearch(int low, int high, ArrayList<Integer> arr1, int arr2sum) 

        if (low > high || low < 0)
            return -1;

        int mid = (low + high) / 2;
        int midVal = arr1.get(mid);

        int sum1 = sum(arr1);
        int resultOfMoveOrg = (sum1 - midVal) - (arr2sum + midVal);
        int resultOfMove = (sum1 - midVal) - (arr2sum + midVal);
        if (resultOfMove < 0)
            resultOfMove = resultOfMove * -1;

        if (resultOfMove < MIN) 
            // lets do the swap
            return mid;
        

        // this is positive number greater than min
        // which mean we should move left
        if (resultOfMoveOrg < 0) 

            // 1,10, 19 ==> 30
            // 100
            // 20, 110 = -90
            // 29, 111 = -83
            return binarySearch(low, mid - 1, arr1, arr2sum);
         else 

            // resultOfMoveOrg > 0
            // 1,5,10, 15, 19, 20 => 70
            // 21
            // For 10
            // 60, 31 it will be 29
            // now if we move 1
            // 71, 22 ==> 49
            // but now if we move 20
            // 50, 41 ==> 9
            return binarySearch(mid + 1, high, arr1, arr2sum);
        
    

    private static int findMin(ArrayList<Integer> arr1) 

        ArrayList<Integer> list2 = new ArrayList<>(arr1.subList(arr1.size() - 1, arr1.size()));
        arr1.remove(arr1.size() - 1);
        while (true) 

            int index = binarySearch(0, arr1.size(), arr1, sum(list2));
            if (index != -1) 
                int val = arr1.get(index);
                arr1.remove(index);
                list2.add(val);
                Collections.sort(list2);
                MIN = diff(arr1, list2);
             else 
                // now try for arr2
                int index2 = binarySearch(0, list2.size(), list2, sum(arr1));
                if (index2 != -1) 

                    int val = list2.get(index2);
                    list2.remove(index2);
                    arr1.add(val);
                    Collections.sort(arr1);

                    MIN = diff(arr1, list2);
                 else 
                    // no switch in both the cases
                    break;
                
            
        

        System.out.println("MIN==>" + MIN);
        System.out.println("arr1==>" + arr1 + ":" + sum(arr1));
        System.out.println("list2==>" + list2 + ":" + sum(list2));
        return 0;
    

    public static void main(String args[]) 

        ArrayList<Integer> org = new ArrayList<>();
        org = new ArrayList<>();
        org.add(1);
        org.add(2);
        org.add(3);
        org.add(7);
        org.add(8);
        org.add(10);

        findMin(org);
    

【讨论】:

【参考方案11】:

您可以通过使用位循环所有可能的组合来使用位来解决此问题: 主要算法:

for(int i = 0; i < 1<<n; i++) 
int s = 0;
for(int j = 0; j < n; j++) 
if(i & 1<<j) s += arr[j];

int curr = abs((total-s)-s);
ans = min(ans, curr);

使用 long long 以获得更大的输入。

但是在这里我找到了一个递归和动态编程解决方案,我使用了这两种方法来解决问题,并且都可以很好地处理更大的输入。希望这会有所帮助:) link to solution

【讨论】:

【参考方案12】:

请检查我为这个问题写的这个逻辑。它适用于我检查过的几个场景。请评论解决方案, 方法:

    对主数组进行排序,分成 2 组。 然后根据代码中提到的条件,通过移位和交换元素从一个数组到另一个数组,开始使团队平等。

如果差是总和的差小于较大数组的最小数(总和较大的数组),则将元素从较大的数组移动到较小的数组。移位发生在条件下,较大数组中的元素的值小于或等于差值。当较大数组中的所有元素都大于差值时,移位停止并发生交换。我只是交换数组的最后一个元素(通过查找要交换的两个元素可以提高效率),但这仍然有效。让我知道这个逻辑是否在任何情况下都失败了。

public class SmallestDifference 
static int sum1 = 0, sum2 = 0, diff, minDiff;
private static List<Integer> minArr1;
private static List<Integer> minArr2;
private static List<Integer> biggerArr;

/**
 * @param args
 */
public static void main(String[] args) 
    SmallestDifference sm = new SmallestDifference();
    Integer[] array1 =  2, 7, 1, 4, 5, 9, 10, 11 ;
    List<Integer> array = new ArrayList<Integer>();
    for (Integer val : array1) 
        array.add(val);
    
    Collections.sort(array);
    CopyOnWriteArrayList<Integer> arr1 = new CopyOnWriteArrayList<>(array.subList(0, array.size() / 2));
    CopyOnWriteArrayList<Integer> arr2 = new CopyOnWriteArrayList<>(array.subList(array.size() / 2, array.size()));
    diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
    minDiff = array.get(0);
    sm.updateSum(arr1, arr2);
    System.out.println(arr1 + " : " + arr2);
    System.out.println(sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
    int k = arr2.size();
    biggerArr = arr2;
    while (diff != 0 && k >= 0) 
        while (diff != 0 && sm.findMin(biggerArr) < diff) 
            sm.swich(arr1, arr2);
            int sum1 = sm.getSum(arr1), sum2 = sm.getSum(arr2);
            diff = Math.abs(sum1 - sum2);
            if (sum1 > sum2) 
                biggerArr = arr1;
             else 
                biggerArr = arr2;
            
            if (minDiff > diff || sm.findMin(biggerArr) > diff) 
                minDiff = diff;
                minArr1 = new CopyOnWriteArrayList<>(arr1);
                minArr2 = new CopyOnWriteArrayList<>(arr2);
            
            sm.updateSum(arr1, arr2);
            System.out.println("Shifting : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
        
        while (k >= 0 && minDiff > array.get(0) && minDiff != 0) 
            sm.swap(arr1, arr2);
            diff = Math.abs(sm.getSum(arr1) - sm.getSum(arr2));
            if (minDiff > diff) 
                minDiff = diff;
                minArr1 = new CopyOnWriteArrayList<>(arr1);
                minArr2 = new CopyOnWriteArrayList<>(arr2);
            
            sm.updateSum(arr1, arr2);
            System.out.println("Swapping : " + sum1 + " - " + sum2 + " = " + diff + " : minDiff = " + minDiff);
            k--;
        
        k--;
    
    System.out.println(minArr1 + " : " + minArr2 + " = " + minDiff);


private void updateSum(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) 
    SmallestDifference sm1 = new SmallestDifference();
    sum1 = sm1.getSum(arr1);
    sum2 = sm1.getSum(arr2);


private int findMin(List<Integer> biggerArr2) 
    Integer min = biggerArr2.get(0);
    for (Integer integer : biggerArr2) 
        if(min > integer) 
            min = integer;
        
    
    return min;


private int getSum(CopyOnWriteArrayList<Integer> arr) 
    int sum = 0;
    for (Integer val : arr) 
        sum += val;
    
    return sum;


private void swap(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) 
    int l1 = arr1.size(), l2 = arr2.size(), temp2 = arr2.get(l2 - 1), temp1 = arr1.get(l1 - 1);
    arr1.remove(l1 - 1);
    arr1.add(temp2);
    arr2.remove(l2 - 1);
    arr2.add(temp1);
    System.out.println(arr1 + " : " + arr2);


private void swich(CopyOnWriteArrayList<Integer> arr1, CopyOnWriteArrayList<Integer> arr2) 
    Integer e;
    if (sum1 > sum2) 
        e = this.findElementJustLessThanMinDiff(arr1);
        arr1.remove(e);
        arr2.add(e);
     else 
        e = this.findElementJustLessThanMinDiff(arr2);
        arr2.remove(e);
        arr1.add(e);
    
    System.out.println(arr1 + " : " + arr2);


private Integer findElementJustLessThanMinDiff(CopyOnWriteArrayList<Integer> arr1) 
    Integer e = arr1.get(0);
    int tempDiff = diff - e;
    for (Integer integer : arr1) 
        if (diff > integer && (diff - integer) < tempDiff) 
            e = integer;
            tempDiff = diff - e;
        
    
    return e;

【讨论】:

【参考方案13】:

这里有一个可能的解决方案-https://***.com/a/31228461/4955513 这个 Java 程序似乎可以解决这个问题,前提是满足一个条件——问题的解决方案只有一个。

【讨论】:

【参考方案14】:
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();




【讨论】:

你前几行的例子错了【参考方案15】:

这里是递归方法

def helper(arr,sumCal,sumTot,n):
    if n==0:
        return abs(abs(sumCal-sumTot)-sumCal)
    
    return min(helper(arr,sumCal+arr[n-1],sumTot,n-1),helper(arr,sumCal,sumTot,n-1))

def minimum_subset_diff(arr,n):
    sum=0
    for i in range(n):
        sum+=arr[i]
        
    return helper(arr,0,sum,n)

这是一种自上而下的动态方法来降低时间复杂度

dp=[[-1]*100 for i in range(100)]
def helper_dp(arr,sumCal,sumTot,n):
    if n==0:
        return abs(abs(sumCal-sumTot)-sumCal)
    
    if dp[n][sumTot]!=-1:
        return dp[n][sumTot]
    
    return min(helper_dp(arr,sumCal+arr[n-1],sumTot,n-1),helper_dp(arr,sumCal,sumTot,n-1))

def minimum_subset_diff_dp(arr,n):
    sum=0
    for i in range(n):
        sum+=arr[i]
        
    return helper_dp(arr,0,sum,n)

【讨论】:

没有解释的代码转储表明你要么不想解释,要么不了解自己但有互联网连接,以便从某个地方提取代码。【参考方案16】:
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;

使用 SumI =0 和 SumE= a 中所有元素的总和调用函数。 这个 O(n!) 解决方案确实计算了我们可以将给定数组分成两部分的方式,使得差异最小。 但由于 n! 绝对不实用!时间复杂度希望使用 DP 来改善这一点。

【讨论】:

【参考方案17】:
#include<bits/stdc++.h>
using namespace std;
bool ison(int i,int x)

 if((i>>x) & 1)return true;
 return false;

int main()

// cout<<"enter the number of elements  : ";
    int n;
    cin>>n;
    int a[n];
    for(int i=0;i<n;i++)
    cin>>a[i];
    int sumarr1[(1<<n)-1];
    int sumarr2[(1<<n)-1];
    memset(sumarr1,0,sizeof(sumarr1));
    memset(sumarr2,0,sizeof(sumarr2));
    int index=0;
    vector<int>v1[(1<<n)-1];
    vector<int>v2[(1<<n)-1];

    for(int i=1;i<(1<<n);i++)
      
       for(int j=0;j<n;j++)
       
          if(ison(i,j))
          
             sumarr1[index]+=a[j];
             v1[index].push_back(a[j]);
          
          else
          
             sumarr2[index]+=a[j];
             v2[index].push_back(a[j]);
          
       index++;
    
    int ans=INT_MAX;
    int ii;
    for(int i=0;i<index;i++)
    
       if(abs(sumarr1[i]-sumarr2[i])<ans)
       
          ii=i;
          ans=abs(sumarr1[i]-sumarr2[i]);
       
    
    cout<<"first partitioned array : ";
    for(int i=0;i<v1[ii].size();i++)
    
       cout<<v1[ii][i]<<" ";
    
    cout<<endl;
    cout<<"2nd partitioned array : ";
    for(int i=0;i<v2[ii].size();i++)
    
       cout<<v2[ii][i]<<" ";
    
    cout<<endl;
    cout<<"minimum difference is : "<<ans<<endl;

【讨论】:

【参考方案18】:

提到了许多关于在非常可接受的时间范围内获得“近似”解决方案的答案。但由于在采访中被问到,我不认为他们需要一个近似算法。我也不认为他们需要一个简单的指数算法。

回到问题,假设数字和的最大值已知,实际上可以使用动态规划在多项式时间内求解。参考这个链接 https://people.cs.clemson.edu/~bcdean/dp_practice/dp_4.swf

【讨论】:

【参考方案19】:
HI I think This Problem can be solved in Linear Time on a sorted array , no Polynomial Time is required , rather than Choosing Next Element u can choose nest two Element and decide which side which element to go. in This Way
in this way minimize the difference, let suppose
0,1,5,6 ,
choose 0,1
0 , 1
choose 5,6
0,6, 1,5
but still that is not exact solution , now at the end there will be difference of sum in 2 array let suppose x
but there can be better solution of difference of (less than x)
for that Find again 1 greedy approach over  sorted half sized array
and move x/2(or nearby) element from 1 set to another or exchange element of(difference x/2) so that difference can be minimized***

【讨论】:

以上是关于如何将一个集合分成两个子集,以使两个集合中数字之和之间的差异最小?的主要内容,如果未能解决你的问题,请参考以下文章

P1466 集合 Subset Sums(01背包求填充方案数)

等和的分隔子集(DP)

poj 3977 子集

真子集和子集如何判断?

如何判断两个集合的关系?

拟合数字的最佳方法