数组“和和/或子”是否为“x”?

时间:2016-09-14 15:09:07

标签: c arrays algorithm performance complexity-theory

目标

我想编写一个算法(在C中),该算法返回TRUEFALSE10),具体取决于数组{{ 1}}输入中给出的“和和/或子”可以A(见下面的说明)。请注意,x的所有值都是在[1,x-1]之间的随机(均匀)采样的整数。

澄清和示例

“sum和/或sub”是指将“+”和“ - ”放在数组的每个元素前面并求和。我们称这个函数为A

SumSub

应该返回int SumSub (int* A,int x) { ... } SumSub({2,7,5},10) 为7-2 + 5 = 10。您会注意到TRUE的第一个元素也可以视为否定,因此A中元素的顺序无关紧要。

A

应该返回SumSub({2,7,5,2},10) ,因为无法“汇总和/或分配”FALSE的元素以达到array的值。请注意,这意味着必须使用x的所有元素。

复杂性

An的长度。如果必须探索所有可能的加号和减号组合,问题的复杂性为O(2 ^ n)。但是,某些组合比其他组合更有可能,因此值得首先探讨(希望输出为A)。通常,需要从最大数字中减去所有元素的组合是不可能的(因为TRUE的所有元素都低于A)。另外,如果x,尝试添加n>x的所有元素是没有意义的。

问题

我该如何写这个函数?

3 个答案:

答案 0 :(得分:2)

不幸的是,您的问题可以缩短为subset-sum problem,即NP-Complete。因此,无法避免指数解。

答案 1 :(得分:1)

正如你所说,原始问题的解决方案确实是指数级的。但是对于A []中的数字,给定范围[1,x-1],您可以使解多项式。有一个非常简单的动态编程解决方案。

订单:

时间复杂度:O(n ^ 2 * x)

记忆复杂度:O(n ^ 2 * x)

其中,n = A []

中的元素数量

您需要为此

使用动态编程方法

您知道可以在[-n x,n x]范围内进行的最小,最大范围。创建一个大小为(n)X(2 * n * x + 1)的二维数组。让我们称之为dp [] []

dp [i] [j] =从[0..i-1]获取A []的所有元素是否可以使值j

所以

dp [10] [3] = 1意味着取A []的前10个元素我们可以创建值3

dp [10] [3] = 0表示取A []的前10个元素我们不能创建值3

这是一种伪代码:

int SumSub (int* A,int x)
{
    bool dp[][];//set all values of this array 0
    dp[0][0] = true;
    for(i=1;i<=n;i++) {
        int val = A[i-1];
        for(j=-n*x;j<=n*x;j++) {
            dp[i][j]=dp[ i-1 ][ j + val ] | dp[ i-1 ][ j - val ];
        }
    }
    return dp[n][x];
}

答案 2 :(得分:0)

不幸的是,即使x被限制为0,这也是NP完全的,所以不要期望多项式时间算法。为了说明这一点,我将简单地从NP-hard Partition Problem中进行简化,它会询问给定的多正整数是否可以被分成具有相等和的两个部分:

假设我们有一个由n个正整数B_1,...,B_n组成的分区问题实例。从此创建一个问题实例,其中A_i = B_i,每个1&lt; = i&lt; = n,并设置x = 0。

显然,如果将B分区分为两个部分C和D具有相等的总和,那么还有一个问题实例的解决方案:在C中的每个数字前放一个+,在D中的每个数字前面-(或反过来)。由于C和D具有相等的和,因此该表达式必须等于0。

OTOH,如果我们刚刚创建的问题实例的解决方案是YES(TRUE),那么我们可以轻松地将B的分区创建为具有相等总和的两个部分:将所有正项放在一个部分中(比方说,C)和所有负面的术语(当然没有前面的-)(比方说,D)。由于我们知道表达式的总值为0,因此必须是C中(正)数的总和等于D中数字的(否定)总和。

因此对任一问题实例的YES表示对另一个问题实例的YES,这反过来暗示对任一问题实例的NO意味着对另一个问题实例的NO - 即两个问题实例具有相同的解决方案。因此,如果有可能在多项式时间内解决您的问题,那么通过构造上述问题实例,使用您的多时间算法求解它并报告,也可以在多项式时间内解决NP难分区问题。它给出的结果。