这是标记为动态编程的problem(给定数字N,找到将其写为两个或更多个连续整数之和的方法的数量)和示例15 = 7 + 8,1 + 2 + 3 + 4 + 5,4 + 5 + 6
我用这样的数学解决了:
a +(a + 1)+(a + 2)+(a + 3)+ ... +(a + k)= N
(k + 1)* a +(1 + 2 + 3 + ... + k)= N
(k + 1) a + k (k + 1)/ 2 = N
(k + 1)*(2 * a + k)/ 2 = N
然后检查如果N可以被(k + 1)和(2 * a + k)整除,那么我可以在O(sqrt(N))时间内找到答案
以下是我的问题,如何通过动态编程来解决这个问题?什么是复杂性(O)?
答案 0 :(得分:1)
对于奇数 N ,此问题相当于查找N的除数不超过sqrt(N)
。 (对于偶数 N ,有几个曲折。)如果您有权访问素数列表O(sqrt(N)/ln(N))
,则该任务需要O(sqrt(N))
。
我不知道动态编程在这里有什么帮助。
答案 1 :(得分:1)
我们可以使用动态编程来计算所有K到N的1 + 2 + 3 + ... + K之和。sum[i]
以下表示总和1 + 2 + 3 + ... +岛
sum = [0]
for i in 1..N:
append sum[i-1] + i to sum
利用这些和,我们可以快速找到求和为N的所有连续整数序列。和i +(i + 1)+(i + 2)+ ... j等于sum[j] - sum[i] + 1
。如果总和小于N,我们递增j
。如果总和大于N,我们递增i
。如果总和等于N,我们会增加计数器i
和j
。
i = 0
j = 0
count = 0
while j <= N:
cur_sum = sum[j] - sum[i] + 1
if cur_sum == N:
count++
if cur_sum <= N:
j++
if cur_sum >= N:
i++
虽然有比使用这种动态编程解决方案更好的选择。可以使用公式k(k + 1)/ 2以数学方式计算sum
数组,因此我们可以在不需要额外存储的情况下即时计算它。更好的是,因为我们只在每次迭代中将最终合并的和的端点移动至少1,所以我们可以通过添加/减去添加/删除的值来更快地计算它。 / p>
i = 0
j = 0
sum = 0
count = 0
while j <= N:
cur_sum = sum[j] - sum[i] + 1
if cur_sum == N:
count++
if cur_sum <= N:
j++
sum += j
if cur_sum >= N:
sum -= i
i++
答案 2 :(得分:1)
被接受的答案很好,但是没有更好的方法。发布我的Java代码如下,以供参考。它可能很冗长,但是可以更清楚地解释这个想法。假设连续的整数都是正数。
private static int count(int n) {
int i = 1, j = 1, count = 0, sum = 1;
while (j<n) {
if (sum == n) { // matched, move sub-array section forward by 1
count++;
sum -= i;
i++;
j++;
sum +=j;
} else if (sum < n) { // not matched yet, extend sub-array at end
j++;
sum += j;
} else { // exceeded, reduce sub-array at start
sum -= i;
i++;
}
}
return count;
}
答案 3 :(得分:0)
为了解决这个问题,我们将尝试[1,M]中所有连续整数的和,其中M来自M(M + 1)/ 2 = N。
int count = 0
for i in [1,M]
for j in [i, M]
s = sum(i, j) // s = i + (i+1) + ... + (j-1) + j
if s == N
count++
if s >= N
break
return count
< p>
由于我们不希望在每次迭代中从头开始计算sum(i, j)
,我们将使用称为“memoization”的技术。让我们创建一个整数矩阵sum[M+1][M+1]
并将sum[i][j]
设置为i +(i + 1)+ ... +(j-1)+ j。
for i in [1, M]
sum[i][i] = i
int count = 0
for i in [1, M]
for j in [i + 1, M]
sum[i][j] = sum[i][j-1] + j
if sum[i][j] == N
count++
if sum[i][j] >= N
break
return count
复杂性显然是O(M ^ 2),即O(N)
答案 4 :(得分:0)
1)对于n> = 0的整数,从0到n的整数之和为n *(n + 1)/ 2。这很经典:首先写下这个总和: S = 0 + 1 + ... + n 然后像这样: S = n +(n-1)+ ... + 0 你看到2 * S等于(0 + n)+(1 + n-1))+ ... +(n + 0)=(n + 1) n,所以S = n < / em>(n + 1)/ 2的确如此。 (众所周知,但我希望我的答案是自成一体的。)
2)从1开始,如果我们注意到cons(m,n)和m +(m + 1)+ ...(n-1)+ n,则posiive之间的整数的连续和(即> = 0)使得1&lt; = m&lt; = n我们看到: cons(m,n)=(0 + 1 + ... + n) - (0 + 1 + ... +(m-1))从1给出: cons(m,n)= n *(n + 1)/ - m(m-1)/ 2
3)然后将问题重新编写如下:在N = cons(m,n)形式中我们用多少种方式写入N,其中m,n整数使得1 <= m&lt; = n?如果我们有N = cons(m,n),这相当于m ^ 2 - m +(2N -n ^ 2 -n)= 0,即实数多项式T ^ 2 - m +(2N -n) ^ 2 -n)有一个真实的根,m:它的判别式delta必须是一个正方形。但我们有:
delta = 1 - 3*(2*N - n^2 - n)
此delta是一个必须是正方形的整数。因此存在整数M,使得:
delta = 1 - 3*(2*N - n^2 - n) = M^2
即
M^2 = 1 - 6*N + n(n+1)
n(n + 1)总是可以分为2(例如,从开始就是我们的S的2倍,但这里有一个更微不足道的原因,在连续的整数中,一个必须是偶数),因此M ^ 2是奇怪,暗示M必须是奇数。
4)重写或上一个等式为:
n^2 + n + (1-6*N - M^2) = 0
这表明实数多项式X ^ 2 + X +(1-6 * N - M ^ 2)具有实数零,n:因此其判别式γ必须是正方形,但是:
gamma = 1 - 4*(1-6*N-M^2)
并且这必须是正方形,因此在这里再次存在整数G,使得
G^2 = 1 - 4*(1-6*N-M^2)
G^2 = 1 + 4*(2*N + m*(m-1))
表明,由于M是奇数,G也是奇数。
5)减法M ^ 2 = 1 - 4 *(2 * N - n *(n + 1))到G ^ 2 = 1 + 4 *(2 * N + m * (m-1)))得到:
G^2 - M^2 = 4*(2*N + m*(m-1)) + 4*(2*N -n*(n+1))
= 16*N + 4*( m*(m-1) - n*(n+1) )
= 16*N - 8*N (because N = cons(m,n))
= 8*N
最后这可以改写为:
(G-M)*(G+M) = 8*N, that is
[(G-M)/2]*[(G+M)/2] = 2*N
其中(G-M)/ 2和(G + M)/ 2是整数(G-M和G + M是偶数,因为G和M是奇数)
6)因此,在每种方式将N写为cons(m,n),我们可以将一种方式(并且只有一种方式,因为M和G唯一确定)与因子2相关联* N到乘积x * y,其中x =(GM)/ 2且y =(G + M)/ 2其中G和M是两个奇数整数。由于G = x + y和M = -x + y,因为G和M是奇数,我们看到x和y应该具有相反的奇偶性。因此,在x和y中,一个是偶数,另一个是奇数。因此2 * N = x * y其中x和y中,一个是偶数而另一个是奇数。让c是x和y中的奇数,d是偶数。然后2 * N = c * d,因此N = c *(d / 2)。因此,只要N = cons(m,n),c是奇数除N,并且由N唯一确定。相反,只要N有一个奇数除数,就可以对所有这些东西进行逆向工程以找到n和m。
7) * 结论:写作方式的数量之间存在一对一的对应关系N = cons(m,n)(这是写N的方式的数量)作为连续整数的总和,如我们所见)和N的奇数除数。 *
8)最后,我们要寻找的数字是n的奇数除数。 我想用DP或其他方法解决这个问题比解决前一个问题更容易。