[LeetCode 829.] 连续整数求和
LeetCode 829. 连续整数求和
其实是一道数学题。把他当作一道待定系数的一元二次方程,然后求其合法整数取值。
题目描述
给定一个正整数 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
解题思路
思路一:双指针
连续整数和,暴力枚举显然是会超时的,考虑用双指针来降低复杂度。
然而双指针在遇到 case 53084025
发生了超时。
双指针代码如下:
class Solution {
public:
int consecutiveNumbersSum(int n) {
int left = 1;
int right = 1;
int64_t sum = 1; // avoid overflow
int cnt = 0;
while (right <= n) {
if (sum == n) {
cnt++;
sum -= left++;
sum += ++right;
} else if (sum < n) {
sum += ++right;
} else if (sum > n) {
sum -= left++;
}
}
return cnt;
} // TLE
};
思路二:数学
既然求和验证会超时,那么有没有办法直接根据结果反推求区间呢?
有的!
对于长度为k的区间 [x, x+1, ..., x+k-1] 其区间和 sum = kx + k(k-1)/2
。根据此公式,我们已知sum也就是n,然后 x>=1
据此可以推出 k 的取值范围是 1<=k<=(sqrt(1+8n)-1)/2
,由此,我们枚举 k 然后验证 x 的合法性即可。
参考代码
int consecutiveNumbersSum(int n) {
// k个连续正整数[x, ..., x+k-1]的求和公式 sum = kx + k(k-1)/2
// 已知sum,枚举k,验证x即可
// x>=1,于是 1<=k<=(sqrt(1+8n)-1)/2
int cnt = 0;
for (int k = (sqrt(1+8ULL*n)-1)/2; k>=1; k--) { // avoid overflow, case 933320757
if ((n - k*(k-1)/2) % k == 0) {
cnt++;
}
}
return cnt;
} // AC
中文力扣评论区甚至有人上传了这么一段代码,神了:
int consecutiveNumbersSum(int N) {
int res = 0;
for (int i = 1; N > 0; N -= i++)
res += (N % i == 0);
return res;
}