将一组数字拆分为成员的子组

时间:2017-03-21 19:39:57

标签: c++

我想问一下如何检查一组数字是否可以分成子组(每个子组必须有3个成员),每个子组成员的总和是否相等。如何检查这么多组合?

示例:

int numbers[] = {1, 2, 5, 6, 8, 3, 2, 4, 5};

可分为

{1, 5, 6}, {2, 8, 2}, {3, 4, 5}

2 个答案:

答案 0 :(得分:2)

可以遵循递归方法,其中一个保留两个数组:

  • 包含每个子组总和的数组。
  • 用于检查元素是否已被接入的布尔数组 某个小组与否。

您要求提供3个子组,即本文其余部分的K = 3,但请记住,在处理递归时,应考虑基础案例。在这种情况下,我们将关注两个基本情况:

  1. 如果K为1,那么我们已经有了答案,只有完整的数组 具有相同总和的子集。
  2. 如果N < K,那么就不可能将数组划分为子集 等于和,因为我们不能将数组分成N个以上的部分。
  3. 如果组的总和不能被K整除,则无法除以它。如果k除以总和,我们将继续。我们的目标是减少将组划分为K个子组,其中每个子组的总和应该是组的总和除以K.

    在下面的代码中,编写了一个递归方法,试图将数组元素添加到某个子集中。如果该子集的总和达到所需的总和,我们递归地迭代下一部分,否则我们回溯不同的元素集。如果总和达到所需总和的子集的数量是(K-1),我们标记可以将数组分成具有相等和的K个部分,因为剩余的元素已经具有等于所需总和的总和。

    引自here,而在您的情况下,您将设置K = 3,如示例代码中所示。

    // C++ program to check whether an array can be
    // subsetitioned into K subsets of equal sum
    #include <bits/stdc++.h>
    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 subsetition or not
        K               - number of subsetitions 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 subsetition 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 subsetition
        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 subsetition 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 subsetitioned 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 subsetitions 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 subsetitions
        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;
        if (subset < subsetSum[0])
            return false;
    
        //  call recursive method to check K-subsetition 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";
    }
    

    输出:

      

    可以分成相等的总和。

    相关链接:23

答案 1 :(得分:0)

在这种特殊情况下你可以做类似的事情(3x3):

const int COUNT = 9;
bool test(int const (&array)[COUNT], std::vector<std::vector<int>>* result) {

    for(int _1=0; _1<COUNT-2; ++_1) {
        for(int _2=1; _2<COUNT-1; ++_2) {
            if(_2 == _1)
                continue;
            for(int _3=2; _3<COUNT; ++_3) {
                if(_3 == _2 || _3 == _1)
                    continue;
                std::vector<int> chosen1 {array[_1], array[_2], array[_3]};
                std::vector<int> rest;
                for(int _x = 0; _x < COUNT; ++_x) {
                    if(_x != _1 && _x != _2 && _x != _3) {
                        rest.push_back(array[_x]);
                    }
                }

                for (int _4 = 0; _4 < COUNT-5; ++_4) {
                    for (int _5 = 1; _5 < COUNT-4; ++_5) {
                        if(_5 == _4)
                            continue;
                        for (int _6 = 2; _6 < COUNT-3; ++_6) {
                            if(_6 == _5 || _6 == _4)
                                continue;
                            std::vector<int> chosen2 = {rest[_4], rest[_5], rest[_6]};
                            std::vector<int> chosen3;
                            for(int _x = 0; _x < COUNT-3; ++_x) {
                                if(_x != _4 && _x != _5 && _x != _6) {
                                    chosen3.push_back(rest[_x]);
                                }
                            }

                            int total = std::accumulate(chosen1.begin(), chosen1.end(), 0);

                            if((std::accumulate(chosen2.begin(), chosen2.end(), 0) == total) &&
                               (std::accumulate(chosen3.begin(), chosen3.end(), 0) == total)) {
                                *result = {chosen1, chosen2, chosen3};
                                return true;
                            }
                        }
                    }
                }
            }
        }
    }
    return false;
}

int main() {
    int values[] = {1, 2, 5, 6, 8, 3, 2, 4, 5};
    std::vector<std::vector<int>> result;
    if(test(values, &result)) {
        for(auto& x : result) {
            std::cout << "{";
            for(auto& y : x) {
                std::cout << y << ",";
            }
            std::cout << "}";
        }
        std::cout << std::endl;
    } else {
        std::cout << "not found";
    }
}

如果您有更长的数组(3 + * 3),那么您可以使用重复(您也可以在我的示例中使用它),但这仍然会很慢。