拆分成最多数目的正偶数之和

标签: 贪心 数学 回溯

难度: Medium

给你一个整数 finalSum 。请你将它拆分成若干个 互不相同 的正偶数之和,且拆分出来的正偶数数目 最多 。

  • 比方说,给你 finalSum = 12 ,那么这些拆分是 符合要求 的(互不相同的正偶数且和为 finalSum):(2 + 10) ,(2 + 4 + 6) 和 (4 + 8) 。它们中,(2 + 4 + 6) 包含最多数目的整数。注意 finalSum 不能拆分成 (2 + 2 + 4 + 4) ,因为拆分出来的整数必须互不相同。

请你返回一个整数数组,表示将整数拆分成 最多 数目的正偶数数组。如果没有办法将 finalSum 进行拆分,请你返回一个  数组。你可以按 任意 顺序返回这些整数。

示例 1:

输入:finalSum = 12
输出:[2,4,6]
解释:以下是一些符合要求的拆分:(2 + 10)(2 + 4 + 6) (4 + 8) 。
(2 + 4 + 6) 为最多数目的整数,数目为 3 ,所以我们返回 [2,4,6] 。
[2,6,4] ,[6,2,4] 等等也都是可行的解。

示例 2:

输入:finalSum = 7
输出:[]
解释:没有办法将 finalSum 进行拆分。
所以返回空数组。

示例 3:

输入:finalSum = 28
输出:[6,8,2,12]
解释:以下是一些符合要求的拆分:(2 + 26)(6 + 8 + 2 + 12)(4 + 24) 。
(6 + 8 + 2 + 12) 有最多数目的整数,数目为 4 ,所以我们返回 [6,8,2,12] 。
[10,2,4,12] ,[6,2,4,16] 等等也都是可行的解。

提示:

  • 1 <= finalSum <= 1010

Submission

运行时间: 358 ms

内存: 25.8 MB

class Solution:
    def maximumEvenSplit(self, finalSum: int) -> List[int]:
        if finalSum%2==1:
            return []
        finalSum = finalSum//2
        s = int((sqrt(8*finalSum+1)-1)/2)
        finalSum_2 = s*(s+1)//2
        res = finalSum-finalSum_2
        result = [2*i for i in range(1,s)]
        result.append((s+res)*2)
        return result
        

Explain

首先检查`finalSum`是否为偶数,因为只有偶数才能被拆分为若干个互不相同的正偶数之和。如果是奇数,则直接返回空数组。接着,将`finalSum`除以2,以简化计算,因为我们关心的是将其分解为连续的正整数之和。然后使用求和公式的逆公式计算出最大的整数`s`,使得从1到`s`的整数之和不超过`finalSum/2`。这是利用了求和公式的逆,通过解方程得到的。之后计算出1到`s`的整数之和,与`finalSum/2`的差值`res`就是最后一个需要添加的数。最终结果需要将这些整数转换回偶数,并添加到结果列表中。特别注意的是最后一个数是`(s + res) * 2`,这样确保了数列中的所有数都是唯一且符合要求的偶数。

时间复杂度: O(s), 其中 s 是满足1到s的整数之和不超过finalSum/2的最大整数

空间复杂度: O(s)

class Solution:
    def maximumEvenSplit(self, finalSum: int) -> List[int]:
        if finalSum % 2 == 1:
            return []  # 如果finalSum为奇数,直接返回空列表
        finalSum = finalSum // 2  # 将finalSum除以2简化计算
        s = int((sqrt(8 * finalSum + 1) - 1) / 2)  # 通过求和公式的逆计算出最大s
        finalSum_2 = s * (s + 1) // 2  # 计算1到s的整数之和
        res = finalSum - finalSum_2  # 计算差值
        result = [2 * i for i in range(1, s)]  # 生成1到s-1的偶数序列
        result.append((s + res) * 2)  # 添加最后一个需要的偶数
        return result  # 返回结果

Explore

算法中使用的求和公式是等差数列求和公式,即前n个自然数的和为n(n+1)/2。为了确定可以用哪个n值来接近但不超过finalSum/2,需要解方程n(n+1)/2 <= finalSum/2。这可以转化为求解二次方程n^2 + n - 2*finalSum/2 = 0。使用二次方程的求根公式(-b±sqrt(b^2-4ac))/2a),我们得到n = (-1 + sqrt(1 + 8*finalSum/2))/2。这里取负的根是不符合实际情况的,因为n应为正值,所以我们使用正的根。这就是求和公式的逆推导过程,确保了我们找到的最大整数s,使得从1到s的整数之和最接近但不超过finalSum/2。

在算法中,通过求和公式的逆公式首先计算出最大的整数s,使得从1到s的整数之和finalSum_2不超过finalSum/2。这保证了我们已经累积了尽可能多的连续整数总和。剩余的差值res = finalSum/2 - finalSum_2表示finalSum/2与1到s的和之间的差异。若添加这个差值作为下一个整数,即s+res,正好可以使总和达到finalSum/2。将这些整数转换为偶数后(即乘以2),确保了分解的偶数和为finalSum。这是因为每个连续整数乘以2得到的是连续偶数,且添加的最后一个偶数确保了总和达到原始的finalSum。

将finalSum除以2的原因是简化计算过程,因为原问题是将finalSum分解为多个不相同的偶数和。因为每一个偶数都是2的倍数,可以将每个偶数表示为2倍的一个整数(例如2, 4, 6表示为2×1, 2×2, 2×3)。因此,问题转化为将finalSum/2分解为若干个不相同的正整数的和。这样的处理简化了问题的复杂性,并且可以直接利用已知的数学公式(如等差数列求和)。最终将计算出的整数序列转换回偶数序列,并检查总和是否等于原始的finalSum,这确保了方法的正确性。因为每一步的转换和计算都保持了逻辑上的一致性和数学上的正确性。