分析均值分割的性质,
设分割后
A
A
A 数组长度为
k
k
k ,那么
B
B
B 数组长度为
n
−
k
n-k
n−k ,
A
A
A 和
B
B
B 平均值相等,有
s
u
m
A
k
=
s
u
m
B
n
−
k
\dfrac {sum_A}{k} = \dfrac{sum_B}{n-k}
ksumA=n−ksumB。除法需要转换浮点数,有精度问题。为了避免精度问题,整理上式。
整理,得
①
s
u
m
A
×
(
n
−
k
)
=
s
u
m
B
×
k
①sum_A\times (n-k) = sum_B\times k
①sumA×(n−k)=sumB×k。
设
n
u
m
s
nums
nums 所有数之和为
s
u
m
sum
sum , 有
②
s
u
m
=
s
u
m
A
+
s
u
m
B
②sum=sum_A+sum_B
②sum=sumA+sumB
①
②
①②
①②联立,得
③
s
u
m
A
×
n
=
s
u
m
×
k
③sum_A\times n=sum\times k
③sumA×n=sum×k
由于
①
<
=
>
③
①<=>③
①<=>③,问题转化为,求与
n
u
m
num
num均值相等的
A
A
A。
A
A
A 是
n
u
m
s
nums
nums 的子数组。原问题:求
n
u
m
s
nums
nums的均值分割数组
A
A
A、
B
B
B。
归一化处理,令 n u m s nums nums 的每一个数 x = n x − s u m x = nx - sum x=nx−sum,避免精度处理。归一化改变 n u m s nums nums 的平均值,为 0 0 0。问题转化为,求均值为 0 0 0 的 A A A 。
用状态压缩表示 A A A 选取了 n u m s nums nums 的哪些数, 1 1 1 表示选, 0 0 0 表示不选。 n u m s nums nums 最大长度 30 30 30 ,可知一共 2 30 2^{30} 230 种状态, T L E TLE TLE 。
于是折半查找,把 n u m s nums nums 均分为 l e f t left left 、 r i g h t right right 两半,对两半分别状态压缩,求均值等于 0 0 0 的 A A A 。设 A A A 的某状态,总和为 t o t a l total total ,如果 B B B 中存在某状态,总和为 − t o t a l -total −total,两个状态之和的均值为 0 0 0 ,这也是一个可行解 。上述过程,和直接查找整个 n u m s nums nums 是等价的。但是状态数量缩减为 2 × 2 15 2\times 2^{15} 2×215
要注意,在 r i g h t right right 中不能选取所有元素,否则相当于选择了整个 n u m s nums nums 作为 A A A 数组,不符合题意。
class Solution {
public:
bool splitArraySameAverage(vector<int>& nums) {
int n = nums.size(), m = n/2;//n是nums的长度,m是左子数组A的长度
if(0==m) return false;//单元素数组//不可分割
int sum = accumulate(nums.begin(),nums.end(),0);
for(auto &x:nums) x = n*x - sum;//归一化//使得avg(nums) = 0//避免浮点精度
unordered_set<int> h;//存所有状态对应的total
for(int i = 1 ;i<(1<<m);i++){//折半搜索,先搜索left
int total = 0;//left某状态的总和
for(int j = 0;j<m;j++)
if((i>>j)&1)
total += nums[j];//第j位存在
if(0 == total) return true;//状态总和为0,sumA=sum,可以分割
h.emplace(total);
}
for(int i = 1;i<(1<<(n-m));i++){//再搜索right
int total = 0;//right某状态的总和
for(int j = 0;j<n-m;j++)
if((i>>j)&1)
total += nums[m+j];
if(0 == total) return true;//sumB = sum
if(i!=(1<<(n-m))-1 && h.count(-total)) return true;
}
return false;
}
};
理解思路很重要!
欢迎读者在评论区留言,作为日更博主,看到就会回复的。