📌  相关文章
📜  将集合分成相等总和的K个子集

📅  最后修改于: 2021-05-24 19:19:48             🧑  作者: Mango

给定N个元素的整数数组,任务是将该数组划分为K个非空子集,以使每个子集中的元素总和相同。该数组的所有元素都应该是一个分区的一部分。
例子:

Input : arr = [2, 1, 4, 5, 6], K = 3
Output : Yes
we can divide above array into 3 parts with equal
sum as [[2, 4], [1, 5], [6]]

Input  : arr = [2, 1, 5, 5, 6], K = 3
Output : No
It is not possible to divide above array into 3
parts with equal sum

我们可以递归地解决此问题,我们为每个分区的和保留一个数组,并为一个布尔数组检查一个元素是否已被带入某个分区。
首先,我们需要检查一些基本情况,
如果K为1,那么我们已经有了答案,完整的数组只是具有相同总和的子集。
如果N 如果数组的和不能被K整除,则无法对数组进行除法。仅当k除以和时,我们才会继续。我们的目标是减少将数组划分为K个部分,其中每个部分的和应为array_sum / K
在下面的代码中,编写了一种递归方法,该方法试图将数组元素添加到某个子集中。如果此子集的总和达到要求的总和,我们将递归迭代下一部分,否则我们将回溯不同的元素集。如果总和达到所需总和的子集数为(K-1),则我们标记为可以将数组划分为具有相等总和的K个部分,因为其余元素的总和已等于所需总和。

C++
// C++ program to check whether an array can be
// partitioned into K subsets of equal sum
#include 
using namespace std;
  
// Recursive Utility method to check K equal sum
// subsetition of array
/**
    array           - given input array
    subsetSum array   - sum to store each subset of the array
    taken           - boolean array to check whether element
                      is taken into sum partition or not
    K               - number of partitions needed
    N               - total number of element in array
    curIdx          - current subsetSum index
    limitIdx        - lastIdx from where array element should
                      be taken */
bool isKPartitionPossibleRec(int arr[], int subsetSum[], bool taken[],
                   int subset, int K, int N, int curIdx, int limitIdx)
{
    if (subsetSum[curIdx] == subset)
    {
        /*  current index (K - 2) represents (K - 1) subsets of equal
            sum last partition will already remain with sum 'subset'*/
        if (curIdx == K - 2)
            return true;
  
        //  recursive call for next subsetition
        return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
                                            K, N, curIdx + 1, N - 1);
    }
  
    //  start from limitIdx and include elements into current partition
    for (int i = limitIdx; i >= 0; i--)
    {
        //  if already taken, continue
        if (taken[i])
            continue;
        int tmp = subsetSum[curIdx] + arr[i];
  
        // if temp is less than subset then only include the element
        // and call recursively
        if (tmp <= subset)
        {
            //  mark the element and include into current partition sum
            taken[i] = true;
            subsetSum[curIdx] += arr[i];
            bool nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
                                            subset, K, N, curIdx, i - 1);
  
            // after recursive call unmark the element and remove from
            // subsetition sum
            taken[i] = false;
            subsetSum[curIdx] -= arr[i];
            if (nxt)
                return true;
        }
    }
    return false;
}
  
//  Method returns true if arr can be partitioned into K subsets
// with equal sum
bool isKPartitionPossible(int arr[], int N, int K)
{
    //  If K is 1, then complete array will be our answer
    if (K == 1)
        return true;
  
    //  If total number of partitions are more than N, then
    // division is not possible
    if (N < K)
        return false;
  
    // if array sum is not divisible by K then we can't divide
    // array into K partitions
    int sum = 0;
    for (int i = 0; i < N; i++)
        sum += arr[i];
    if (sum % K != 0)
        return false;
  
    //  the sum of each subset should be subset (= sum / K)
    int subset = sum / K;
    int subsetSum[K];
    bool taken[N];
  
    //  Initialize sum of each subset from 0
    for (int i = 0; i < K; i++)
        subsetSum[i] = 0;
  
    //  mark all elements as not taken
    for (int i = 0; i < N; i++)
        taken[i] = false;
  
    // initialize first subsubset sum as last element of
    // array and mark that as taken
    subsetSum[0] = arr[N - 1];
    taken[N - 1] = true;
  
    //  call recursive method to check K-substitution condition
    return isKPartitionPossibleRec(arr, subsetSum, taken,
                                     subset, K, N, 0, N - 1);
}
  
//  Driver code to test above methods
int main()
{
    int arr[] = {2, 1, 4, 5, 3, 3};
    int N = sizeof(arr) / sizeof(arr[0]);
    int K = 3;
  
    if (isKPartitionPossible(arr, N, K))
        cout << "Partitions into equal sum is possible.\n";
    else
        cout << "Partitions into equal sum is not possible.\n";
}


Java
// Java program to check whether an array can be
// partitioned into K subsets of equal sum
class GFG 
{
  
// Recursive Utility method to check K equal sum
// subsetition of array
/**
    array         - given input array
    subsetSum array - sum to store each subset of the array
    taken         - boolean array to check whether element
                    is taken into sum partition or not
    K             - number of partitions needed
    N             - total number of element in array
    curIdx         - current subsetSum index
    limitIdx     - lastIdx from where array element should
                    be taken */
static boolean isKPartitionPossibleRec(int arr[], int subsetSum[], boolean taken[],
                int subset, int K, int N, int curIdx, int limitIdx)
{
    if (subsetSum[curIdx] == subset)
    {
        /* current index (K - 2) represents (K - 1) subsets of equal
            sum last partition will already remain with sum 'subset'*/
        if (curIdx == K - 2)
            return true;
  
        // recursive call for next subsetition
        return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
                                            K, N, curIdx + 1, N - 1);
    }
  
    // start from limitIdx and include elements into current partition
    for (int i = limitIdx; i >= 0; i--)
    {
        // if already taken, continue
        if (taken[i])
            continue;
        int tmp = subsetSum[curIdx] + arr[i];
  
        // if temp is less than subset then only include the element
        // and call recursively
        if (tmp <= subset)
        {
            // mark the element and include into current partition sum
            taken[i] = true;
            subsetSum[curIdx] += arr[i];
            boolean nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
                                            subset, K, N, curIdx, i - 1);
  
            // after recursive call unmark the element and remove from
            // subsetition sum
            taken[i] = false;
            subsetSum[curIdx] -= arr[i];
            if (nxt)
                return true;
        }
    }
    return false;
}
  
// Method returns true if arr can be partitioned into K subsets
// with equal sum
static boolean isKPartitionPossible(int arr[], int N, int K)
{
    // If K is 1, then complete array will be our answer
    if (K == 1)
        return true;
  
    // If total number of partitions are more than N, then
    // division is not possible
    if (N < K)
        return false;
  
    // if array sum is not divisible by K then we can't divide
    // array into K partitions
    int sum = 0;
    for (int i = 0; i < N; i++)
        sum += arr[i];
    if (sum % K != 0)
        return false;
  
    // the sum of each subset should be subset (= sum / K)
    int subset = sum / K;
    int []subsetSum = new int[K];
    boolean []taken = new boolean[N];
  
    // Initialize sum of each subset from 0
    for (int i = 0; i < K; i++)
        subsetSum[i] = 0;
  
    // mark all elements as not taken
    for (int i = 0; i < N; i++)
        taken[i] = false;
  
    // initialize first subsubset sum as last element of
    // array and mark that as taken
    subsetSum[0] = arr[N - 1];
    taken[N - 1] = true;
  
    // call recursive method to check K-substitution condition
    return isKPartitionPossibleRec(arr, subsetSum, taken,
                                    subset, K, N, 0, N - 1);
}
  
// Driver code 
public static void main(String[] args)
{
    int arr[] = {2, 1, 4, 5, 3, 3};
    int N = arr.length;
    int K = 3;
  
    if (isKPartitionPossible(arr, N, K))
        System.out.println("Partitions into equal sum is possible.");
    else
        System.out.println("Partitions into equal sum is not possible.");
}
}
  
// This code is contributed by Princi Singh


Python3
# Python3 program to check whether an array can be 
# partitioned into K subsets of equal sum 
  
# Recursive Utility method to check K equal sum 
# subsetition of array 
  
"""* 
array     - given input array 
subsetSum array - sum to store each subset of the array 
taken     - ean array to check whether element 
is taken into sum partition or not 
K         - number of partitions needed 
N         - total number of element in array 
curIdx     - current subsetSum index 
limitIdx     - lastIdx from where array element should 
be taken """
  
def isKPartitionPossibleRec(arr, subsetSum, taken, 
                            subset, K, N, curIdx, limitIdx):
    if subsetSum[curIdx] == subset:
          
        """ current index (K - 2) represents (K - 1) 
        subsets of equal sum last partition will 
        already remain with sum 'subset'"""
        if (curIdx == K - 2):
            return True
          
        # recursive call for next subsetition 
        return isKPartitionPossibleRec(arr, subsetSum, taken, 
                                       subset, K, N, curIdx + 1 , N - 1)
      
    # start from limitIdx and include 
    # elements into current partition 
    for i in range(limitIdx, -1, -1):
          
        # if already taken, continue 
        if (taken[i]):
            continue
        tmp = subsetSum[curIdx] + arr[i] 
          
        # if temp is less than subset, then only 
        # include the element and call recursively 
        if (tmp <= subset):
              
            # mark the element and include into 
            # current partition sum 
            taken[i] = True
            subsetSum[curIdx] += arr[i] 
            nxt = isKPartitionPossibleRec(arr, subsetSum, taken, 
                                          subset, K, N, curIdx, i - 1)
                                            
            # after recursive call unmark the element and 
            # remove from subsetition sum 
            taken[i] = False
            subsetSum[curIdx] -= arr[i] 
            if (nxt):
                return True
    return False
  
# Method returns True if arr can be 
# partitioned into K subsets with equal sum 
def isKPartitionPossible(arr, N, K):
      
    # If K is 1,
    # then complete array will be our answer 
    if (K == 1):
        return True
      
    # If total number of partitions are more than N, 
    # then division is not possible 
    if (N < K):
        return False
          
    # if array sum is not divisible by K then 
    # we can't divide array into K partitions 
    sum = 0
    for i in range(N):
        sum += arr[i] 
    if (sum % K != 0):
        return False
      
    # the sum of each subset should be subset (= sum / K) 
    subset = sum // K 
    subsetSum = [0] * K 
    taken = [0] * N
      
    # Initialize sum of each subset from 0 
    for i in range(K):
        subsetSum[i] = 0
          
    # mark all elements as not taken 
    for i in range(N):
        taken[i] = False
          
    # initialize first subsubset sum as  
    # last element of array and mark that as taken 
    subsetSum[0] = arr[N - 1] 
    taken[N - 1] = True
      
    # call recursive method to check 
    # K-substitution condition 
    return isKPartitionPossibleRec(arr, subsetSum, taken, 
                                   subset, K, N, 0, N - 1)
      
# Driver Code
arr = [2, 1, 4, 5, 3, 3 ]
N = len(arr) 
K = 3
if (isKPartitionPossible(arr, N, K)):
    print("Partitions into equal sum is possible.\n")
else:
    print("Partitions into equal sum is not possible.\n")
  
# This code is contributed by SHUBHAMSINGH8410


C#
// C# program to check whether an array can be
// partitioned into K subsets of equal sum
using System;
  
class GFG
{
      
// Recursive Utility method to check K equal sum
// subsetition of array
/**
    array     - given input array
    subsetSum array - sum to store each subset of the array
    taken     - boolean array to check whether element
                    is taken into sum partition or not
    K         - number of partitions needed
    N         - total number of element in array
    curIdx     - current subsetSum index
    limitIdx     - lastIdx from where array element should
                    be taken */
static bool isKPartitionPossibleRec(int []arr, int []subsetSum, bool []taken,
                int subset, int K, int N, int curIdx, int limitIdx)
{
    if (subsetSum[curIdx] == subset)
    {
        /* current index (K - 2) represents (K - 1) subsets of equal
            sum last partition will already remain with sum 'subset'*/
        if (curIdx == K - 2)
            return true;
  
        // recursive call for next subsetition
        return isKPartitionPossibleRec(arr, subsetSum, taken, subset,
                                            K, N, curIdx + 1, N - 1);
    }
  
    // start from limitIdx and include elements into current partition
    for (int i = limitIdx; i >= 0; i--)
    {
        // if already taken, continue
        if (taken[i])
            continue;
        int tmp = subsetSum[curIdx] + arr[i];
  
        // if temp is less than subset then only include the element
        // and call recursively
        if (tmp <= subset)
        {
            // mark the element and include into current partition sum
            taken[i] = true;
            subsetSum[curIdx] += arr[i];
            bool nxt = isKPartitionPossibleRec(arr, subsetSum, taken,
                                            subset, K, N, curIdx, i - 1);
  
            // after recursive call unmark the element and remove from
            // subsetition sum
            taken[i] = false;
            subsetSum[curIdx] -= arr[i];
            if (nxt)
                return true;
        }
    }
    return false;
}
  
// Method returns true if arr can be partitioned into K subsets
// with equal sum
static bool isKPartitionPossible(int []arr, int N, int K)
{
    // If K is 1, then complete array will be our answer
    if (K == 1)
        return true;
  
    // If total number of partitions are more than N, then
    // division is not possible
    if (N < K)
        return false;
  
    // if array sum is not divisible by K then we can't divide
    // array into K partitions
    int sum = 0;
    for (int i = 0; i < N; i++)
        sum += arr[i];
    if (sum % K != 0)
        return false;
  
    // the sum of each subset should be subset (= sum / K)
    int subset = sum / K;
    int []subsetSum = new int[K];
    bool []taken = new bool[N];
  
    // Initialize sum of each subset from 0
    for (int i = 0; i < K; i++)
        subsetSum[i] = 0;
  
    // mark all elements as not taken
    for (int i = 0; i < N; i++)
        taken[i] = false;
  
    // initialize first subsubset sum as last element of
    // array and mark that as taken
    subsetSum[0] = arr[N - 1];
    taken[N - 1] = true;
  
    // call recursive method to check K-substitution condition
    return isKPartitionPossibleRec(arr, subsetSum, taken,
                                    subset, K, N, 0, N - 1);
}
  
// Driver code 
static public void Main ()
{
      
    int []arr = {2, 1, 4, 5, 3, 3};
    int N = arr.Length;
    int K = 3;
  
    if (isKPartitionPossible(arr, N, K))
        Console.WriteLine("Partitions into equal sum is possible.");
    else
        Console.WriteLine("Partitions into equal sum is not possible.");
}
}
  
// This code is contributed by ajit.


输出:

Partitions into equal sum is possible.