📌  相关文章
📜  将集合划分为两个子集,以使子集总和的差异最小

📅  最后修改于: 2021-05-07 06:39:43             🧑  作者: Mango

给定一组整数,任务是将其分为两组S1和S2,以使它们的和之间的绝对差最小。
如果存在一个包含n个元素的集合S,则假定Subset1具有m个元素,Subset2必须具有nm个元素,并且abs(sum(Subset1)– sum(Subset2))的值应该最小。
例子:

Input:  arr[] = {1, 6, 11, 5} 
Output: 1
Explanation:
Subset1 = {1, 5, 6}, sum of Subset1 = 12 
Subset2 = {11}, sum of Subset2 = 11        

这个问题主要是对动态编程的扩展。设置18(分区问题)。
递归解决方案
递归方法是从数组的所有值生成所有可能的和,并检查哪种解决方案是最佳的。
要生成总和,我们要么将第i个项目包含在集合1中,要么不包含,即将其包含在集合2中。

C
// A Recursive C program to solve minimum sum partition
// problem.
#include 
using namespace std;
 
// Function to find the minimum sum
int findMinRec(int arr[], int i, int sumCalculated, int sumTotal)
{
    // If we have reached last element.  Sum of one
    // subset is sumCalculated, sum of other subset is
    // sumTotal-sumCalculated.  Return absolute difference
    // of two sums.
    if (i==0)
        return abs((sumTotal-sumCalculated) - sumCalculated);
 
 
    // For every item arr[i], we have two choices
    // (1) We do not include it first set
    // (2) We include it in first set
    // We return minimum of two choices
    return min(findMinRec(arr, i-1, sumCalculated+arr[i-1], sumTotal),
               findMinRec(arr, i-1, sumCalculated, sumTotal));
}
 
// Returns minimum possible difference between sums
// of two subsets
int findMin(int arr[], int n)
{
    // Compute total sum of elements
    int sumTotal = 0;
    for (int i=0; i


Java
// JAVA code to partition a set into two subsets
// such that the difference of subset sums
// is minimum
import java.util.*;
 
class GFG {
     
    // Function to find the minimum sum
    public static int findMinRec(int arr[], int i,
                                int sumCalculated,
                                 int sumTotal)
    {
        // If we have reached last element.
        // Sum of one subset is sumCalculated,
        // sum of other subset is sumTotal-
        // sumCalculated.  Return absolute
        // difference of two sums.
        if (i == 0)
            return Math.abs((sumTotal-sumCalculated) -
                                   sumCalculated);
      
      
        // For every item arr[i], we have two choices
        // (1) We do not include it first set
        // (2) We include it in first set
        // We return minimum of two choices
        return Math.min(findMinRec(arr, i - 1, sumCalculated
                                   + arr[i-1], sumTotal),
                                 findMinRec(arr, i-1,
                                  sumCalculated, sumTotal));
    }
      
    // Returns minimum possible difference between
    // sums of two subsets
    public static int findMin(int arr[], int n)
    {
        // Compute total sum of elements
        int sumTotal = 0;
        for (int i = 0; i < n; i++)
            sumTotal += arr[i];
      
        // Compute result using recursive function
        return findMinRec(arr, n, 0, sumTotal);
    }
     
    /* Driver program to test above function */
    public static void main(String[] args)
    {
        int arr[] = {3, 1, 4, 2, 2, 1};
        int n = arr.length;
        System.out.print("The minimum difference"+
                        " between two sets is " +
                         findMin(arr, n));
    }
}
   
// This code is contributed by Arnav Kr. Mandal.


Python3
# Python3 program for the
# above approach
# A Recursive C program to
# solve minimum sum partition
# problem.
 
# Function to find the minimum sum
def findMinRec(arr, i, sumCalculated,
               sumTotal):
 
    # If we have reached last element. 
    # Sum of one subset is sumCalculated,
    # sum of other subset is sumTotal-
    # sumCalculated.  Return absolute
    # difference of two sums.
    if (i == 0):
        return abs((sumTotal - sumCalculated) -
                    sumCalculated)
 
    # For every item arr[i], we have two choices
    # (1) We do not include it first set
    # (2) We include it in first set
    # We return minimum of two choices
    return min(findMinRec(arr, i - 1,
                          sumCalculated+arr[i - 1],
                          sumTotal),
               findMinRec(arr, i - 1,
                          sumCalculated, sumTotal))
 
# Returns minimum possible
# difference between sums
# of two subsets
def findMin(arr,  n):
 
    # Compute total sum
    # of elements
    sumTotal = 0
    for i in range(n):
        sumTotal += arr[i]
 
    # Compute result using
    # recursive function
    return findMinRec(arr, n,
                      0, sumTotal)
 
# Driver code
if __name__ == "__main__":
 
    arr = [3, 1, 4, 2, 2, 1]
    n = len(arr)
    print("The minimum difference " +
          "between two sets is ",
           findMin(arr, n))
 
# This code is contributed by Chitranayal


C#
// C# code to partition a set into two subsets
// such that the difference of subset sums
// is minimum
using System;
 
class GFG {
     
    // Function to find the minimum sum
    public static int findMinRec(int []arr, int i,
                                int sumCalculated,
                                     int sumTotal)
    {
        // If we have reached last element.
        // Sum of one subset is sumCalculated,
        // sum of other subset is sumTotal-
        // sumCalculated. Return absolute
        // difference of two sums.
        if (i == 0)
            return Math.Abs((sumTotal-sumCalculated)
                                    - sumCalculated);
     
     
        // For every item arr[i], we have two choices
        // (1) We do not include it first set
        // (2) We include it in first set
        // We return minimum of two choices
        return Math.Min(findMinRec(arr, i - 1,
                  sumCalculated + arr[i-1], sumTotal),
                   findMinRec(arr, i-1, sumCalculated,
                                           sumTotal));
    }
     
    // Returns minimum possible difference between
    // sums of two subsets
    public static int findMin(int []arr, int n)
    {
         
        // Compute total sum of elements
        int sumTotal = 0;
        for (int i = 0; i < n; i++)
            sumTotal += arr[i];
     
        // Compute result using recursive function
        return findMinRec(arr, n, 0, sumTotal);
    }
     
    /* Driver program to test above function */
    public static void Main()
    {
        int []arr = {3, 1, 4, 2, 2, 1};
        int n = arr.Length;
        Console.Write("The minimum difference"+
                        " between two sets is " +
                               findMin(arr, n));
    }
}
     
// This code is contributed by nitin mittal.


Javascript


C++
// A Recursive C program to solve minimum sum partition
// problem.
#include 
using namespace std;
 
// Returns the minimum value of the difference of the two sets.
int findMin(int arr[], int n)
{
    // Calculate sum of all elements
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += arr[i];
 
    // Create an array to store results of subproblems
    bool dp[n+1][sum+1];
 
    // Initialize first column as true. 0 sum is possible
    // with all elements.
    for (int i = 0; i <= n; i++)
        dp[i][0] = true;
 
    // Initialize top row, except dp[0][0], as false. With
    // 0 elements, no other sum except 0 is possible
    for (int i = 1; i <= sum; i++)
        dp[0][i] = false;
 
    // Fill the partition table in bottom up manner
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=sum; j++)
        {
            // If i'th element is excluded
            dp[i][j] = dp[i-1][j];
 
            // If i'th element is included
            if (arr[i-1] <= j)
                dp[i][j] |= dp[i-1][j-arr[i-1]];
        }
    }
  
    // Initialize difference of two sums.
    int diff = INT_MAX;
     
    // Find the largest j such that dp[n][j]
    // is true where j loops from sum/2 t0 0
    for (int j=sum/2; j>=0; j--)
    {
        // Find the
        if (dp[n][j] == true)
        {
            diff = sum-2*j;
            break;
        }
    }
    return diff;
}
 
// Driver program to test above function
int main()
{
    int arr[] = {3, 1, 4, 2, 2, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout << "The minimum difference between 2 sets is "
         << findMin(arr, n);
    return 0;
}


Java
// A Recursive java program to solve
// minimum sum partition problem.
import java.io.*;
 
class GFG
{
    // Returns the minimum value of
    //the difference of the two sets.
    static int findMin(int arr[], int n)
    {
        // Calculate sum of all elements
        int sum = 0;
        for (int i = 0; i < n; i++)
            sum += arr[i];
     
        // Create an array to store
        // results of subproblems
        boolean dp[][] = new boolean[n + 1][sum + 1];
     
        // Initialize first column as true.
        // 0 sum is possible  with all elements.
        for (int i = 0; i <= n; i++)
            dp[i][0] = true;
     
        // Initialize top row, except dp[0][0],
        // as false. With 0 elements, no other
        // sum except 0 is possible
        for (int i = 1; i <= sum; i++)
            dp[0][i] = false;
     
        // Fill the partition table
        // in bottom up manner
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= sum; j++)
            {
                // If i'th element is excluded
                dp[i][j] = dp[i - 1][j];
     
                // If i'th element is included
                if (arr[i - 1] <= j)
                    dp[i][j] |= dp[i - 1][j - arr[i - 1]];
            }
        }
     
        // Initialize difference of two sums.
        int diff = Integer.MAX_VALUE;
         
        // Find the largest j such that dp[n][j]
        // is true where j loops from sum/2 t0 0
        for (int j = sum / 2; j >= 0; j--)
        {
            // Find the
            if (dp[n][j] == true)
            {
                diff = sum - 2 * j;
                break;
            }
        }
        return diff;
    }
     
    // Driver program
    public static void main (String[] args)
    {
        int arr[] = {3, 1, 4, 2, 2, 1};
        int n = arr.length;
        System.out.println ("The minimum difference between 2 sets is "
                            + findMin(arr, n));
     
    }
}
// This code is contributed by vt_m


Python3
# A Recursive Python3 program to solve
# minimum sum partition problem.
import sys
 
# Returns the minimum value of the
# difference of the two sets.
def findMin(a, n):
     
    su = 0
     
    # Calculate sum of all elements
    su = sum(a)
 
    # Create an 2d list to store
    # results of subproblems
    dp = [[0 for i in range(su + 1)]
             for j in range(n + 1)]
 
    # Initialize first column as true.
    # 0 sum is possible
    # with all elements.
    for i in range(n + 1):
        dp[i][0] = True
         
    # Initialize top row, except dp[0][0],
    # as false. With 0 elements, no other
    # sum except 0 is possible
    for j in range(1, su + 1):
        dp[0][j] = False
     
    # Fill the partition table in
    # bottom up manner
    for i in range(1, n + 1):
        for j in range(1, su + 1):
             
            # If i'th element is excluded
            dp[i][j] = dp[i - 1][j]
             
            # If i'th element is included
            if a[i - 1] <= j:
                dp[i][j] |= dp[i - 1][j - a[i - 1]]
     
    # Initialize difference
    # of two sums.
    diff = sys.maxsize
 
    # Find the largest j such that dp[n][j]
    # is true where j loops from sum/2 t0 0
    for j in range(su // 2, -1, -1):
        if dp[n][j] == True:
            diff = su - (2 * j)
            break
             
    return diff
     
# Driver code
a = [ 3, 1, 4, 2, 2, 1 ]
n = len(a)
     
print("The minimum difference between "
      "2 sets is ", findMin(a, n))
 
# This code is contributed by Tokir Manva


C#
// A Recursive C# program to solve
// minimum sum partition problem.
using System;
 
class GFG{
     
// Returns the minimum value of
//the difference of the two sets.
static int findMin(int []arr, int n)
{
     
    // Calculate sum of all elements
    int sum = 0;
    for(int i = 0; i < n; i++)
        sum += arr[i];
 
    // Create an array to store
    // results of subproblems
    bool [,]dp = new bool[n + 1, sum + 1];
 
    // Initialize first column as true.
    // 0 sum is possible  with all elements.
    for(int i = 0; i <= n; i++)
        dp[i, 0] = true;
 
    // Initialize top row, except dp[0,0],
    // as false. With 0 elements, no other
    // sum except 0 is possible
    for(int i = 1; i <= sum; i++)
        dp[0, i] = false;
 
    // Fill the partition table
    // in bottom up manner
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= sum; j++)
        {
             
            // If i'th element is excluded
            dp[i, j] = dp[i - 1, j];
 
            // If i'th element is included
            if (arr[i - 1] <= j)
                dp[i, j] |= dp[i - 1, j - arr[i - 1]];
        }
    }
 
    // Initialize difference of two sums.
    int diff = int.MaxValue;
     
    // Find the largest j such that dp[n,j]
    // is true where j loops from sum/2 t0 0
    for(int j = sum / 2; j >= 0; j--)
    {
         
        // Find the
        if (dp[n, j] == true)
        {
            diff = sum - 2 * j;
            break;
        }
    }
    return diff;
}
 
// Driver code
public static void Main(String[] args)
{
    int []arr = { 3, 1, 4, 2, 2, 1 };
    int n = arr.Length;
     
    Console.WriteLine("The minimum difference " +
                      "between 2 sets is " +
                      findMin(arr, n));
}
}
 
// This code is contributed by Rajput-Ji


输出:

The minimum difference between two sets is 1

时间复杂度:

All the sums can be generated by either 
(1) including that element in set 1.
(2) without including that element in set 1.
So possible combinations are :-  
arr[0]      (1 or 2)  -> 2 values
arr[1]    (1 or 2)  -> 2 values
.
.
.
arr[n]     (2 or 2)  -> 2 values
So time complexity will be 2*2*..... *2 (For n times),
that is O(2^n).

动态编程
当元素之和不太大时,可以使用动态编程解决该问题。我们可以创建一个2D数组dp [n + 1] [sum + 1],其中n是给定集合中元素的数量,而sum是所有元素的总和。我们可以自下而上地构造解决方案。

The task is to divide the set into two parts. 
We will consider the following factors for dividing it. 
Let 
  dp[n+1][sum+1] = {1 if some subset from 1st to i'th has a sum 
                      equal to j
                   0 otherwise}
    
    i ranges from {1..n}
    j ranges from {0..(sum of all elements)}  

So      
    dp[n+1][sum+1]  will be 1 if 
    1) The sum j is achieved including i'th item
    2) The sum j is achieved excluding i'th item.

Let sum of all the elements be S.  

To find Minimum sum difference, w have to find j such 
that Min{sum - j*2  : dp[n][j]  == 1 } 
    where j varies from 0 to sum/2

The idea is, sum of S1 is j and it should be closest
to sum/2, i.e., 2*j should be closest to sum.

下面是上述代码的实现。

C++

// A Recursive C program to solve minimum sum partition
// problem.
#include 
using namespace std;
 
// Returns the minimum value of the difference of the two sets.
int findMin(int arr[], int n)
{
    // Calculate sum of all elements
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += arr[i];
 
    // Create an array to store results of subproblems
    bool dp[n+1][sum+1];
 
    // Initialize first column as true. 0 sum is possible
    // with all elements.
    for (int i = 0; i <= n; i++)
        dp[i][0] = true;
 
    // Initialize top row, except dp[0][0], as false. With
    // 0 elements, no other sum except 0 is possible
    for (int i = 1; i <= sum; i++)
        dp[0][i] = false;
 
    // Fill the partition table in bottom up manner
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=sum; j++)
        {
            // If i'th element is excluded
            dp[i][j] = dp[i-1][j];
 
            // If i'th element is included
            if (arr[i-1] <= j)
                dp[i][j] |= dp[i-1][j-arr[i-1]];
        }
    }
  
    // Initialize difference of two sums.
    int diff = INT_MAX;
     
    // Find the largest j such that dp[n][j]
    // is true where j loops from sum/2 t0 0
    for (int j=sum/2; j>=0; j--)
    {
        // Find the
        if (dp[n][j] == true)
        {
            diff = sum-2*j;
            break;
        }
    }
    return diff;
}
 
// Driver program to test above function
int main()
{
    int arr[] = {3, 1, 4, 2, 2, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    cout << "The minimum difference between 2 sets is "
         << findMin(arr, n);
    return 0;
}

Java

// A Recursive java program to solve
// minimum sum partition problem.
import java.io.*;
 
class GFG
{
    // Returns the minimum value of
    //the difference of the two sets.
    static int findMin(int arr[], int n)
    {
        // Calculate sum of all elements
        int sum = 0;
        for (int i = 0; i < n; i++)
            sum += arr[i];
     
        // Create an array to store
        // results of subproblems
        boolean dp[][] = new boolean[n + 1][sum + 1];
     
        // Initialize first column as true.
        // 0 sum is possible  with all elements.
        for (int i = 0; i <= n; i++)
            dp[i][0] = true;
     
        // Initialize top row, except dp[0][0],
        // as false. With 0 elements, no other
        // sum except 0 is possible
        for (int i = 1; i <= sum; i++)
            dp[0][i] = false;
     
        // Fill the partition table
        // in bottom up manner
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= sum; j++)
            {
                // If i'th element is excluded
                dp[i][j] = dp[i - 1][j];
     
                // If i'th element is included
                if (arr[i - 1] <= j)
                    dp[i][j] |= dp[i - 1][j - arr[i - 1]];
            }
        }
     
        // Initialize difference of two sums.
        int diff = Integer.MAX_VALUE;
         
        // Find the largest j such that dp[n][j]
        // is true where j loops from sum/2 t0 0
        for (int j = sum / 2; j >= 0; j--)
        {
            // Find the
            if (dp[n][j] == true)
            {
                diff = sum - 2 * j;
                break;
            }
        }
        return diff;
    }
     
    // Driver program
    public static void main (String[] args)
    {
        int arr[] = {3, 1, 4, 2, 2, 1};
        int n = arr.length;
        System.out.println ("The minimum difference between 2 sets is "
                            + findMin(arr, n));
     
    }
}
// This code is contributed by vt_m

Python3

# A Recursive Python3 program to solve
# minimum sum partition problem.
import sys
 
# Returns the minimum value of the
# difference of the two sets.
def findMin(a, n):
     
    su = 0
     
    # Calculate sum of all elements
    su = sum(a)
 
    # Create an 2d list to store
    # results of subproblems
    dp = [[0 for i in range(su + 1)]
             for j in range(n + 1)]
 
    # Initialize first column as true.
    # 0 sum is possible
    # with all elements.
    for i in range(n + 1):
        dp[i][0] = True
         
    # Initialize top row, except dp[0][0],
    # as false. With 0 elements, no other
    # sum except 0 is possible
    for j in range(1, su + 1):
        dp[0][j] = False
     
    # Fill the partition table in
    # bottom up manner
    for i in range(1, n + 1):
        for j in range(1, su + 1):
             
            # If i'th element is excluded
            dp[i][j] = dp[i - 1][j]
             
            # If i'th element is included
            if a[i - 1] <= j:
                dp[i][j] |= dp[i - 1][j - a[i - 1]]
     
    # Initialize difference
    # of two sums.
    diff = sys.maxsize
 
    # Find the largest j such that dp[n][j]
    # is true where j loops from sum/2 t0 0
    for j in range(su // 2, -1, -1):
        if dp[n][j] == True:
            diff = su - (2 * j)
            break
             
    return diff
     
# Driver code
a = [ 3, 1, 4, 2, 2, 1 ]
n = len(a)
     
print("The minimum difference between "
      "2 sets is ", findMin(a, n))
 
# This code is contributed by Tokir Manva

C#

// A Recursive C# program to solve
// minimum sum partition problem.
using System;
 
class GFG{
     
// Returns the minimum value of
//the difference of the two sets.
static int findMin(int []arr, int n)
{
     
    // Calculate sum of all elements
    int sum = 0;
    for(int i = 0; i < n; i++)
        sum += arr[i];
 
    // Create an array to store
    // results of subproblems
    bool [,]dp = new bool[n + 1, sum + 1];
 
    // Initialize first column as true.
    // 0 sum is possible  with all elements.
    for(int i = 0; i <= n; i++)
        dp[i, 0] = true;
 
    // Initialize top row, except dp[0,0],
    // as false. With 0 elements, no other
    // sum except 0 is possible
    for(int i = 1; i <= sum; i++)
        dp[0, i] = false;
 
    // Fill the partition table
    // in bottom up manner
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= sum; j++)
        {
             
            // If i'th element is excluded
            dp[i, j] = dp[i - 1, j];
 
            // If i'th element is included
            if (arr[i - 1] <= j)
                dp[i, j] |= dp[i - 1, j - arr[i - 1]];
        }
    }
 
    // Initialize difference of two sums.
    int diff = int.MaxValue;
     
    // Find the largest j such that dp[n,j]
    // is true where j loops from sum/2 t0 0
    for(int j = sum / 2; j >= 0; j--)
    {
         
        // Find the
        if (dp[n, j] == true)
        {
            diff = sum - 2 * j;
            break;
        }
    }
    return diff;
}
 
// Driver code
public static void Main(String[] args)
{
    int []arr = { 3, 1, 4, 2, 2, 1 };
    int n = arr.Length;
     
    Console.WriteLine("The minimum difference " +
                      "between 2 sets is " +
                      findMin(arr, n));
}
}
 
// This code is contributed by Rajput-Ji

输出:

The minimum difference between 2 sets is 1

时间复杂度= O(n * sum),其中n是元素的数量,sum是所有元素的总和。