LeetCode 805. 数组的均值分割

给定的整数数组 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].

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/split-array-with-same-average
 

解法

折半搜索,状态压缩,二分查找/双指针。由题意可设数组B中有K个元素,那么

即数组B的平均值等于数组A的平均值。我们可以先对数组进行预处理,数组A中的元素先减去数组A的平均值。那么就转化为求数组A的子序列中求和为0的子序列。求子序列的问题,根据A 数组的长度范围为 [1, 30],可以知道用折半查找+状态压缩。

将数组A分为前后两半,每一半利用状态压缩求和。此时答案可能出现的地方为:

1)前半部分中有子集的和为0;

2)后半部分中有子集的和为0;

3)前半部分某一子集+后半部分某一子集的和为0;

 我们将状态压缩中为数位为1的B数组中的数。此题还有许多细节问题:如B数组和C数组都不为空,那么在判断第三种情况时,要是有解的话,前半部分不能全不贡献也不能为全贡献,全不贡献的话,代表后半部分必有子集为0,满足条件2。全贡献的话同样代表后半部分必有子集为0(属于数组C的部分)。后半部分同理。

class Solution {

	const double eps = 1e-2;

public:
	bool splitArraySameAverage(vector<int>& nums) {
		int n = nums.size();
		if (n == 1 || (n == 2 && nums[0] != nums[1]))return false;
		int total = 0;
		for (auto &num : nums)
			total += num;
		vector<double>dnums;
		for (auto &num : nums)
			dnums.push_back(num - total*1.0 / n);
		int len1 = n / 2, len2 = n - len1;

		auto getSum = [&](int x, int start, int len){
			double sum = 0;
			//int cnt = 0;
			for (int i = 0; i < len; ++i)
			{
				if (x & 1 << i)
				{
					sum += dnums[start + i];
					//++cnt;
				}
			}
			return sum;
		};

		vector<double> v1;

		for (int mask = 1; mask <(1 << len1); ++mask) //从1开始
		{
			double sum = getSum(mask, 0, len1);
			if (fabs(sum)<=eps)
				return true;
			v1.push_back(sum);
		}
        v1.pop_back();    //移除全贡献
		sort(v1.begin(), v1.end());
		//double target=total*1.0/n;
        vector<double> v2;
        for (int mask = 1; mask <(1 << len2); ++mask) //同理
		{
			double sum = getSum(mask, len1, len2);
			if (fabs(sum)<=eps)
				return true;
			v2.push_back(sum);
		}
		v2.pop_back();
        sort(v2.begin(), v2.end());
        int vlen1=v1.size(), vlen2=v2.size();
        //cout<<v1<<" "<<
        int id1=0, id2=vlen2-1;          //双指针:在两个有序数组中寻找目标值
        while(id1<vlen1&&id2>=0)
        {
            double temp=v1[id1]+v2[id2]; //注意double
            if(fabs(temp)<=eps)
                return true;
            else if(temp>0)
                id2--;
            else 
                id1++;
        }
		return false;
	}
};

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值