Leetcode0829. 连续整数求和(difficult)

目录

1. 问题描述

2. 思路与算法1

3. 思路与算法2


1. 问题描述

给定一个正整数 n,返回 连续正整数满足所有数字之和为 n 的组数

例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 1 <= n <= 10^9

2. 思路与算法1

        首先进行必要的数学分析。

        以下进行分类讨论。考虑n是否能表达为k个连续整数之和。

        【case1:考虑k为奇数】

        显然必须满足条件n能够被k整除,记m=\frac{n}{k}。这个不是充要条件,还需要满足m > \frac{k-1}{2}。这是因为如果n表达为k个连续整数之和且k为奇数,则m为这连续k个整数的中间数,所以m的左边至少必须要还有\frac{k-1}{2}个正整数。

        【case2:考虑k为偶数】      

        如果k个整数之和为n,记前半部分的和为x1,后半部分的和为x2,必然有x2=x1+(\frac{k}{2})^2,即:x1 = \frac{1}{2} (n-(\frac{k}{2})^2)

        进一步x1必须能够表达为k/2个连续整数之和。这个成为原问题的一个较小规模的问题,因此可以考虑以动态规划方法(递归+Memoization)来实现。

        在最外层对k进行扫描即可,但是扫描的上限需要注意。概算如下:考虑k为奇数,则中间那个数为m=n/k,这k个连续整数应为{ m-\frac{k-1}{2},...m,...,m+\frac{k-1}{2}},由此可得:

n = m\cdot k > \frac{k-1}{2}k \Rightarrow k < \sqrt{2n}

class Solution:
    def consecutiveNumbersSum(self, n: int) -> int:
        memo = dict()
        def dp(m:int ,k:int) -> bool:
            #print('dp: ',m,k)
            if k==1: 
                return True
            if (m,k) in memo:
                return memo[(m,k)] 
            if k%2 == 1:
                # k为奇数
                memo[(m,k)] = m%k==0 and ((m//k) > ((k-1)//2))
                return memo[(m,k)]
            else:
                # k为偶数       
                tmp = m-(k//2)**2
                if tmp % 2 == 1:
                    memo[(m,k)] = False
                    return False
                else:
                    memo[(m,k)] = dp( tmp // 2, k//2 )
                    return memo[(m,k)]

        cnt = 0        
        for k in range(1,int(sqrt(2*n))+1):
            cnt += dp(n,k)
        #print(memo)
        return cnt

        

        执行用时:672 ms, 在所有 Python3 提交中击败了5.39%的用户

        内存消耗:167.6 MB, 在所有 Python3 提交中击败了5.39%的用户

3. 思路与算法2

        看了看官解,关于k为偶数的分析还能更进一步,如下所示。

        令x为该k个连续整数中的最小正整数,则有n = \frac{k(2x+k-1)}{2},所以必然有k不能整除n但是能整除2n。也就是说“k不能整除n但是能整除2n”是“n可以表达为k个连续整数之和”的必要条件。

        接下来证明“k不能整除n但是能整除2n”其实是“n可以表达为k个连续整数之和”的充分条件。

        由于“k不能整除n但是能整除2n”,所以2n/k一定是奇数(否则就与n不能被k整除矛盾了),令\frac{2n}{k}=2m+1,其中m是整数,则有\frac{n}{k} = m + 1/2。此时,

        \begin{align} 2m+1 &= 2x+k-1 \\ 2x &= 2m +2 - k \\ x &= m + 1 - k/2 \end{align}

        由于k是偶数,因此x也肯定是整数,因此,n可以表达为从x开始的k个连续整数之和。

        

class Solution:
    def consecutiveNumbersSum(self, n: int) -> int:
        cnt = 0        
        for k in range(1,int(sqrt(2*n))+1):
            if k%2 == 1:
                # k为奇数
                #cnt += n%k==0 and ((n//k) > ((k-1)//2))
                cnt += n%k==0 # 后面那个约束条件不需要,因为上面扫描的上界决定了它一定会满足
            else:
                cnt += (n % k) != 0 and (2*n % k ) == 0
        return cnt

        执行用时:116 ms, 在所有 Python3 提交中击败了53.92%的用户

        内存消耗:15.1 MB, 在所有 Python3 提交中击败了6.37%的用户

        回到总目录:Leetcode每日一题总目录(动态更新。。。)

猜你喜欢

转载自blog.csdn.net/chenxy_bwave/article/details/125109674