给定一组整数,任务是将其分为两组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是所有元素的总和。