给定的整数数组 A ,我们要将 A数组 中的每个元素移动到 B数组 或者 C数组中。(B数组和C数组在开始的时候都为空)
返回true
,当且仅当在我们的完成这样的移动后,可使得B数组的平均值和C数组的平均值相等,并且B数组和C数组都不为空。
示例: 输入: [1,2,3,4,5,6,7,8] 输出: true 解释: 我们可以将数组分割为 [1,4,5,8] 和 [2,3,6,7], 他们的平均值都是4.5。
注意:
A
数组的长度范围为[1, 30]
.A[i]
的数据范围为[0, 10000]
.
思路:n有30这么大,直接二进制枚举必然爆炸,我们考虑采用经典的折半搜索方法,这种方法是很常见的,这里我不过多赘述,并且本题我采用二进制折半枚举的方法解决该题目。
首先我们回到本题,我们需要将问题转化,使得我们能够使用折半枚举的方法,首先我们假设给B数组选择了k个数字,则C数组的数字个数就一定是n-k,因此我们得到了一个条件:sum(B)/k=sum(C)/(n-k). 我们将其移项整理得到sum(B)/k=sum(A)/n。此时我们发现 B
的平均值与 A
的平均值相等。因此我们可以将 A
中的每个元素减去它们的平均值,这样 A
的平均值变为 0
。此时我们的问题变成:找出若干个元素组成集合 B
,这些元素的和为 0
。此外,“将 A
中每个元素减去它们的平均值” 这一步会引入浮点数,可能会导致判断的时候出现误差。一种解决方案是使用分数代替浮点数,但这样代码编写起来较为麻烦。更好的解决方案是先将 A
中的每个元素乘以 A
的长度,这样它们的平均值一定为整数。
class Solution {
public boolean splitArraySameAverage(int[] A) {
int sum=0;
int len=A.length/2;
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<A.length;i++) {
A[i]*=A.length;
sum+=A[i];
}
int avg=sum/A.length;
for(int i=0;i<(1<<len);i++) {
int sm=0,nm=0;
for(int j=0;j<len;j++)
if(((i>>j)&1)==1) {
sm+=A[j]-avg;
nm++;
}
map.put(sm, nm);
}
for(int i=0;i<(1<<len);i++) {
int sm=0,nm=0;
for(int j=0;j<len;j++)
if(((i>>j)&1)==1) {
sm+=A[j+len]-avg;
nm++;
}
if(map.containsKey(-sm) && nm+map.get(-sm)<A.length && nm+map.get(-sm)>0)
return true;
}
return false;
}
}