【问题标题】:Does the array “sum and/or sub” to `x`?数组是否“总和和/或子”到`x`?
【发布时间】:2017-01-22 11:12:58
【问题描述】:

目标

我想编写一个算法(在C 中),它返回TRUEFALSE10)取决于输入中给出的数组A 是否可以“求和和/或子”到x(请参阅下文以了解说明)。请注意,A 的所有值都是介于 [1,x-1] 之间的整数,它们是随机(均匀)采样的。

说明和示例

“和/或子”是指在数组的每个元素前面放置“+”和“-”并求和。我们称这个函数为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 的值。请注意,这意味着必须使用A 的所有元素。

复杂性

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

问题

我应该如何编写这个函数?

【问题讨论】:

  • 听起来像是一个典型的背包问题。数组的元素和大小有限制吗?
  • 我想使用我的资源可以处理的大小数组(就 CPU 时间而言)。 A 的元素在 [1,x-1] 之间有界,其中x 可以是任何无符号整数。
  • 应该在 sum 和/或 sub 操作中使用数组中的 所有 值吗?
  • @trincot 是的,必须使用所有值
  • 在这种情况下,这个问题与数学问题的不同之处在于:(为简单起见,假设 unsigned 是 8 位的。)考虑 SumSub({250, 250, 5, 5} 254).. 对于 C 来说这是真的 @ 987654350@ 溢出定义明确 - 只需添加 4 个元素。它在数学上是错误的,其范围是无限的。不确定 OP 想要什么,数学解决方案还是 C 解决方案。

标签: c arrays algorithm performance complexity-theory


【解决方案1】:

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

【讨论】:

  • 答案总是NO的问题也可以“简化为NP-Complete的子集和问题”。 ​(只需输出{2,3},4。)​因此,这种减少的存在对Remi.b的问题没有任何难度。​ ​ ​
【解决方案2】:

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

随订单:

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

内存复杂度:O(n^2*x)

其中,n=A[] 中的元素个数

您需要为此使用动态编程方法

您知道可以在 [-nx,nx] 范围内设定的最小、最大范围。创建一个大小为 (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];
}

【讨论】:

    【解决方案3】:

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

    假设我们有一个由 n 个正整数 B_1, ..., B_n 组成的分区问题的实例。从此创建您的问题的一个实例,其中 A_i = B_i 对于每个 1

    很明显,如果将 B 划分为总和相等的两部分 C 和 D,那么您的问题实例也有一个解决方案:在 C 中的每个数字前面放一个 +,然后在前面加上一个 @ 987654323@ 在 D 中的每个数字前面(或相反)。由于 C 和 D 的和相等,所以这个表达式必须等于 0。

    OTOH,如果我们刚刚创建的问题实例的解决方案是“是”(TRUE),那么我们可以轻松地将 B 划分为具有相等总和的两部分:只需将所有正项放在一个部分中(比如说,C),以及另一个(比如说,D)中的所有否定项(当然没有前面的-)。既然我们知道表达式的总和是0,那么一定是C中的(正)数之和等于D中的(负)数之和。

    因此,对任何一个问题实例的“是”意味着对另一个问题实例的“是”,这反过来意味着对任何一个问题实例的“否”意味着对另一个问题实例的“否”——也就是说,两个问题实例具有相同的解决方案.因此,如果可以在多项式时间内解决您的问题,那么也可以在多项式时间内解决 NP-hard Partition Problem,方法是构建您的问题的上述实例,使用您的多时间算法解决它,然后报告它给出的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-10-12
      • 2022-01-12
      • 2020-12-29
      • 1970-01-01
      • 2020-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多