加载中...
829-连续整数求和(Consecutive Numbers Sum)
发表于:2021-12-03 | 分类: 困难
字数统计: 1.2k | 阅读时长: 5分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/consecutive-numbers-sum

英文原文

Given an integer n, return the number of ways you can write n as the sum of consecutive positive integers.

 

Example 1:

Input: n = 5
Output: 2
Explanation: 5 = 2 + 3

Example 2:

Input: n = 9
Output: 3
Explanation: 9 = 4 + 5 = 2 + 3 + 4

Example 3:

Input: n = 15
Output: 4
Explanation: 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5

 

Constraints:

  • 1 <= n <= 109

中文题目

给定一个正整数 N,试求有多少组连续正整数满足所有数字之和为 N?

例 1:

输入: 5
输出: 2
解释: 5 = 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5。

示例 2:

输入: 9
输出: 3
解释: 9 = 9 = 4 + 5 = 2 + 3 + 4

示例 3:

输入: 15
输出: 4
解释: 15 = 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5

说明: 1 <= N <= 10 ^ 9

通过代码

官方题解

方法一:枚举 [超出时间限制]

我们枚举连续正整数的开始值 start,并进行累加,直到结果大于等于 N。如果结果刚好等于 N,我们就找到了一组答案。

例如当 N = 6 时,若开始值为 1,我们会累加得到 1 + 2 + 3 = 6,得到一组答案;若开始值为 2,我们会累加得到 2 + 3 + 4 = 9,超出了 6。以此类推,直到开始值超过 N

这种方法会超出时间限制。

[sol1]
class Solution { public int consecutiveNumbersSum(int N) { int ans = 0; for (int start = 1; start <= N; ++start) { int target = N, x = start; while (target > 0) target -= x++; if (target == 0) ans++; } return ans; } }
[sol1]
class Solution(object): def consecutiveNumbersSum(self, N): ans = 0 for start in xrange(1, N+1): target = N while target > 0: target -= start start += 1 if target == 0: ans += 1 return ans

复杂度分析

  • 时间复杂度:$O(N\sqrt{N})$。

  • 空间复杂度:$O(1)$。

方法二:简单数学 [超出时间限制]

假设 $k$ 个连续正整数的和为 $N$,即 $N = (x + 1) + (x + 2) + \cdots + (x + k)$,其中 $x \geq 0$,$k \geq 1$。上式经过拆分可以得到 $N = kx + \frac{1}{2}k(k + 1)$,即 $2N = k(2x + k + 1)$。

我们可以枚举 $k$,根据上面的等式,$k$ 的取值范围为 $1 \leq k \leq 2N$。对于每一个 $k$,我们计算出 $x = \frac{1}{2}(\frac{2N}{k} - k - 1)$,如果得到的 $x$ 是一个非负整数,那么我们就找到了一组解。

[sol2]
class Solution { public int consecutiveNumbersSum(int N) { // 2N = k(2x + k + 1) int ans = 0; for (int k = 1; k <= 2*N; ++k) if (2 * N % k == 0) { int y = 2 * N / k - k - 1; if (y % 2 == 0 && y >= 0) ans++; } return ans; } }
[sol2]
class Solution(object): def consecutiveNumbersSum(self, N): # 2N = k(2x + k + 1) ans = 0 for k in xrange(1, 2*N + 1): if 2*N % k == 0: y = 2 * N / k - k - 1 if y % 2 == 0 and y >= 0: ans += 1 return ans

复杂度分析

  • 时间复杂度:$O(N)$。

  • 空间复杂度:$O(1)$。

方法三:进阶数学

在 $2N = k(2x + k + 1)$ 中,我们可以发现 $k < 2x + k + 1$,因此有 $k < \sqrt{2N}$,即我们只需要枚举 $1 \leq k \leq \lfloor \sqrt{2N} \rfloor$ 即可,此时通过枚举可以通过本题。

我们还可以继续挖掘一些性质。由于 $k$ 和 $2x + k + 1$ 的奇偶性不同,此时将 $2N$ 写成 $2^\alpha * M$ 的形式,其中 $\alpha$ 为 $2N$ 中因子 $2$ 的个数,$M$ 为一个奇数。对于 $M$ 的一种拆分 $M = a * b, a \leq b$,可以将 $2N$ 分成奇数 $a$ 和偶数 $2^\alpha * b$ 或者奇数 $b$ 和偶数 $2^\alpha * a$,每一种分配方法中,将小的那个数给 $k$,大的那个数给 $2x + k + 1$,就对应了一组解,那么一种拆分方法对应了两组解。如果不限制 $a \leq b$,那么可以看作一种拆分方法对应了一组解。有一种特殊情况是 $a = b$,此时这种拆分方法只对应了一组解,但仍然和之前的对应(一对一)相同。因此,我们只需要求出 $M$ 的拆分方法即可,其中 $M$ 为 $N$ 的最大奇因子。$M$ 的拆分方法等价于 $M$ 的因子个数。

[sol3]
class Solution { public int consecutiveNumbersSum(int N) { while ((N & 1) == 0) N >>= 1; int ans = 1, d = 3; while (d * d <= N) { int e = 0; while (N % d == 0) { N /= d; e++; } ans *= e + 1; d += 2; } if (N > 1) ans <<= 1; return ans; } }
[sol3]
class Solution(object): def consecutiveNumbersSum(self, N): while N & 1 == 0: N >>= 1 ans = 1 d = 3 while d * d <= N: e = 0 while N % d == 0: N /= d e += 1 ans *= e + 1 d += 2 if N > 1: ans *= 2 return ans

复杂度分析

  • 时间复杂度:$O(\sqrt{N})$。

  • 空间复杂度:$O(1)$。

统计信息

通过次数 提交次数 AC比率
9157 24928 36.7%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
827-最大人工岛(Making A Large Island)
下一篇:
830-较大分组的位置(Positions of Large Groups)
本文目录
本文目录