【问题标题】:Distinct n numbers so that sum equals to N不同的 n 个数字,使总和等于 N
【发布时间】:2017-09-19 09:22:37
【问题描述】:

假设我想在 1 到 N 的范围内找到 n 个不同的数字,使它们的总和等于 N。例如

n = 3, N = 10: the numbers will be (2, 3, 5);
n = 4, N = 10: the numbers will be (1, 2, 3, 4).

虽然找出这个问题的所有可能组合需要指数级的时间,但我正在寻找“最小”的组合,即最大的数字是最小的。例如,

n = 4, and N = 12的情况下,(6, 3, 2, 1) and (5, 4, 2, 1)都可以解决,但我只对(5, 4, 2, 1)感兴趣。

对于这个问题,会不会有时间复杂度更好的算法?我听说过对数合并,但不确定如何在此处应用。

如果需要指定问题的任何细节,请告诉我。并且总是,我们将非常感谢任何帮助。

【问题讨论】:

  • 对于n = 3,N = 10,我认为解决方案应该是2, 3, 5

标签: algorithm


【解决方案1】:

这个问题有一个简单的贪心算法。

首先,有n个元素按升序排列,为了使它们不同,每个元素都应该比它的前一个元素至少大一个。

所以,我们有

1, 2, 3 ... , n

现在,所有n 数字的总和是n*(n + 1)/2

剩下的是left = N - n*(n + 1)/2

为了使最后一个元素尽可能小,我们需要将left的差异分散到所有数字

所以,我们有

1 + left/n, 2 + left/n, ..., n + left/n

如果left % n != 0,我们只需要在最后一个left % n元素上加1。

注意:如果N < n*(n + 1)/2,则无解

例子:

所以,对于 n = 4 和 N = 12

First, we start with

1, 2, 3, 4

left = 12 - (4*5/2) = 2

So, now we have

1 + (2/4), 2 + (2/4), 3 + (2/4), 4 + (2/4) = 1, 2, 3, 4

As left % n = 2
Finally, we have

1, 2, 3 + 1, 4 + 1 = 1, 2, 4, 5

同样,对于 n = 3,N = 10

First, we start with

1, 2, 3

left = 10 - (3*4/2) = 4

So, now we have

1 + (4/3), 2 + (4/3), 3 + (4/3) = 2, 3, 4

As left % n = 1
Finally, we have

2, 3, 4 + 1 = 2, 3, 5

伪代码,时间复杂度O(n)

int[]result = new int[n];
int left = N - n*(n + 1)/2;

for(int i = 0; i < n; i++){
    result[i] = i + 1 + left/n;
    if(i >= n - (left % n)){//Add extra one for last left % n elements
        result[i]++;
    }
}
return result;

【讨论】:

  • 您还可以添加,如果 left 为 0 返回结果,对于 left
  • 简单而聪明!
  • 非常感谢,很清楚,解答了问题
【解决方案2】:

假设您有一个函数 f(n, N, sum) - 它会返回结果,该结果将向您展示从 1N 范围内的 n 元素总和到 sum 的可能性。

至少现在您只需调用f(n, N, N) 即可确定解决方案是否存在。

假设对于给定的nNsum,问题p(n,N,sum) 有一个解决方案,而x 是结果中最小的最大数。那么问题p(n',N',sum')n'=n-1N'=x-1sum'=sum-x应该也有解决办法。问题p(0,N,0)总是有解决办法的,这是归纳的基础。

函数f(n, N, sum) 实际上会返回x 范围内1N 的最小数字,它可以是解决方案的一部分(否则它应该返回-1 或指示没有解决方案的东西)。我们可以尝试从1N 的每个号码作为x 并检查f(n - 1, x - 1, sum - x) 是否有解决方案。

这里的关键是使用 memoization 以避免多次计算相同的函数。只记得找到x。记住每个可能的输入组合最多需要O(n * N * N) 空间和相同的O(n * N * N) 时间来计算。此外,如果sum&gt;N*(N+1)/2 我们可能会立即删除此类呼叫,则将没有解决方案。这是多项式时间/空间复杂度,优于指数。

【讨论】:

    【解决方案3】:

    让我们试着解决这个问题,假设我们在 [1, N] 之间有 n 个不同的数字。什么是最小可能的总和,它将是前 n 个自然数的总和,即

    1 + 2 + 3 + 4 + ... + n
    

    可能的最大数量是

    (N-n+1) + (N-n+2) + (N-n+3) + ... + (N-n+n)
    

    请注意,最小值和最大值之间的所有总和都可以使用 n 个数字进行计算

    现在检查该值是否可行。假设我们取数字 1 到 n。当前总和 S 等于

    S = 1 + 2 + ... + n
    

    如果我将 x 添加到每个元素,它将变成

    S = 1 + 2 + ... + n + x*n <= N
    

    如果我们选择最大可能的 x,那么总和可以通过将 1 个元素添加到从最大的所有数字开始直到达到所需的总和(即最多 n-1 个数字)。这会给我一个有效的答案。

    所以如果答案是可能的,那就是

    1. 取元素从 1 到 n
    2. 为每个元素添加 x
    3. 从最大的数继续加1直到总和达到

    【讨论】:

      【解决方案4】:

      这几乎可以立即计算出来。答案总是看起来像是以N/n 为中心的一系列数字,或者是一系列具有基本相同中心的数字,中间某处有 1 的间隙。

      所以计算那个中间值,然后计算你是否需要以及在哪里需要那个间隙。然后你就完成了。

      如果n 是奇数,那么中间是N/n(假设是整数除法),并且向右移动了足够的 1 来计算余数。因此,例如,如果N 是 10000,n 是 17,那么中间是 588,您的起始范围是 588 ± (17-1)/2,即 580-596。但余数是 4,所以将 4 移过来,你的答案将是 580-592、594-597。

      如果n 是从半数开始的中间。所以中间是(N-n/2)/n + 0.5。因此,例如,如果N 是 10000,n 是 18,那么中间是 555.5,您的起始范围是 555.5 ± (18-1)/2,即 547-564。但是那个除法的余数是 1,所以我们得到的答案是 547-563, 565。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-12
        • 2017-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-16
        • 2020-09-23
        • 2020-12-18
        相关资源
        最近更新 更多