力扣(LeetCode)805. 数组的均值分割(C++)

状态压缩+折半搜索

分析均值分割的性质,
设分割后 A A A 数组长度为 k k k ,那么 B B B 数组长度为 n − k n-k nk 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=nksumB。除法需要转换浮点数,有精度问题。为了避免精度问题,整理上式。
整理,得 ① s u m A × ( n − k ) = s u m B × k ①sum_A\times (n-k) = sum_B\times k sumA×(nk)=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=nxsum,避免精度处理。归一化改变 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;
    }
};

博主致语

理解思路很重要!
欢迎读者在评论区留言,作为日更博主,看到就会回复的。

AC

AC

复杂度分析
  1. 时间复杂度: O ( n × 2 n 2 ) O(n\times 2^{\frac n 2}) O(n×22n) n n n n u m s nums nums 的长度,一共 O ( 2 × 2 n 2 ) O(2\times 2^{\frac n 2}) O(2×22n) 种状态。遍历所有状态的时间复杂度 O ( 2 n 2 ) O(2^{\frac n 2}) O(22n) ,同时遍历每个状态的所有位置的时间复杂度 O ( n ) O(n) O(n),二者是乘积关系。
  2. 空间复杂度: O ( 2 n 2 ) O(2^{\frac n 2}) O(22n) h h h 哈希集合的空间复杂度是 O ( 2 n 2 ) O(2^{\frac n 2}) O(22n) 。因为要存储 l e f t left left 中所有状态的 t o t a l total total
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清墨韵染

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值