如何将数组划分为N个最小和子集?

时间:2016-05-23 14:01:34

标签: algorithm

如何将整数数组划分为N个子集,使这些子集的总和最小?

例如,数组由11个元素组成,我需要6个子集。

{2,1,1,3,4,4,3,2,1,2,3}

子集:{2,1,1,3}, {4}, {4,3}, {3,2}, {1,2}, {3}最小总和= 7。

另一种答案:{2,1,1} {3,4} {4} {3,2} {1,2} {3}最低金额= 7。

注意:分区时必须保持数字在原始集中的显示顺序。

1 个答案:

答案 0 :(得分:1)

一种可能的方法是二元搜索答案。

我们需要一个程序来检查我们是否可以仅使用等于或低于参数S的和来对集合进行分区。我们将此过程称为onlySumsBelow(S)

我们可以使用贪婪的解决方案来实施onlySumsBelow(S)。总是在每个子集中添加尽可能多的元素,并在达到大于S的总和之前停止(我假设我们没有负面元素,这可能会使讨论复杂化)。如果我们无法使用允许的子集数量到达序列的末尾,则总和无效(它太小)。

function onlySumsBelow(S) {
    partitionsUsed = 1;
    currentSum = 0;

    for each value in sequence {
        if (value > S) return false;

        if (currentSum + value > S) {
            // start a new partition
            currentSum = value;  
            partitionsUsed++;   
        } else {
            currentSum += value;
        }
    }

    return partitionsUsed <= N;
}

一旦我们有onlySumsBelow(S)程序,我们就可以二进制搜索答案,从左边的一个区间开始,该区间的值确保搜索到的答案不在下面(例如 0)并且在右端有足够大的数字,以确保搜索到的答案不在上面(例如序列中所有数字的总和)。

如果不考虑效率,那么您可以简单地尝试多个候选答案,而不是二元搜索,从足够小的值开始,例如所有数字的总和除以N,并且然后增加1,直到达到一个好的解决方案。

备注:在问题的最后没有注释(限制我们考虑输入中出现在相邻位置的数字子集)问题是NP完全的,因为它是Partition problem的概括,它只使用两组。