1. 问题背景与理解
今天遇到一道有趣的算法题:LeetCode 805题"数组的均值分割"。题目要求我们判断一个整数数组是否能被分割成两个非空子集,使得这两个子集的平均值相等。乍看之下这像是个简单的数学问题,但实际编码时却有不少值得深究的地方。
先来看题目给出的函数签名:
java复制public boolean splitArraySameAverage(int[] nums)
举个例子,对于输入数组[1,2,3,4,5,6,7,8],我们可以将其分割为[1,4,5,8]和[2,3,6,7],两个子集的平均值都是4.5,因此应该返回true。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数学原理分析
2.1 关键数学转化
这个问题本质上是要找到两个子集A和B,满足:
- A ∪ B = 原始数组
- A ∩ B = ∅
- sum(A)/|A| = sum(B)/|B|
经过数学推导,我们可以将这个条件转化为:
sum(A)/|A| = (totalSum - sum(A))/(n - |A|)
进一步简化可以得到:
sum(A) * (n - k) = (totalSum - sum(A)) * k
=> sum(A) * n = totalSum * k
=> sum(A) = totalSum * k / n
其中k是子集A的大小,n是数组总长度。这意味着我们需要找到一个子集A,其元素和正好等于(totalSum * k)/n,且k的取值范围是1到n-1。
2.2 可行性条件
从这个等式我们可以得出几个重要结论:
- (totalSum * k)必须能被n整除,否则这样的子集不可能存在
- 我们需要检查k从1到n/2的所有可能值(因为k和n-k是对称的)
3. 算法设计与实现
3.1 动态规划解法
这个问题可以转化为一个变种的子集和问题。我们需要检查是否存在某个k,使得数组中有k个元素的和等于(totalSum * k)/n。
具体实现步骤:
- 计算数组总和totalSum和长度n
- 检查是否存在k∈[1, n/2],使得(totalSum * k) % n == 0
- 对于每个满足条件的k,检查是否存在大小为k的子集,其和为target = (totalSum * k)/n
ja复制
