[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;
}
posted @ 2021-09-23 10:50  与MPI做斗争  阅读(169)  评论(0编辑  收藏  举报