假设我想在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)
感兴趣。
对于这个问题,是否会有一个具有更好时间复杂度的算法?我听说过对数合并,但不知道如何在这里应用。
如果需要指明问题的任何细节,请告诉我。总是,任何帮助都将非常感激。
答案 0 :(得分:7)
这个问题有一个简单的贪婪算法。
首先,有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;
答案 1 :(得分:0)
我们假设您有一个功能f(n, N, sum)
- 它将返回结果,该结果将显示从n
范围内1
获取N
元素的可能性总结为sum
。
至少现在您可以确定解决方案是否存在,只需调用f(n, N, N)
。
让我们说对于给定的n
,N
和sum
,问题p(n,N,sum)
有一个解决方案,x
是最小的结果中的数字。然后问题p(n',N',sum')
与n'=n-1
,N'=x-1
,sum'=sum-x
也应该有一个解决方案。问题p(0,N,0)
总是有一个解决方案,它是归纳的基础。
函数f(n, N, sum)
实际上会返回范围x
到1
的最小数字N
,它可以是解决方案的一部分(否则它应该返回-1
或表示解决方案缺席的事情)。我们可以尝试1
到N
的每个号码作为x
,并检查f(n - 1, x - 1, sum - x)
是否有解决方案。
这里的关键是使用 memoization 以便不多次计算相同的功能。记得发现x
。记住每个可能的输入组合最多需要O(n * N * N)
个空间和相同的O(n * N * N)
时间来计算。此外,如果sum>N*(N+1)/2
我们可以立即修剪此类电话,则无法解决问题。这是多项式时间/空间复杂度,优于指数。
答案 2 :(得分:0)
让我们尝试解决问题,假设我们在[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个数字),可以得到总和。这将给我一个有效的答案。
所以如果答案可能就是
答案 3 :(得分:0)
这几乎可以立即计算出来。答案总是看起来像是以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的答案。