蓝桥杯真题演练——数组分割

问题描述

小蓝有一个长度为 N 的数组 A=[A0,A1,...,AN−1]A=[A0​,A1​,...,AN−1​]。现在小蓝想要从 A 对应的数组下标所构成的集合 I=0,1,2,...,N−1 中找出一个子集 R1 ,那么 R1​ 在 I 中的补集为 R2。记 S1=∑r∈R1Ar,S2=∑r∈R2Ar,我们要求 S1​ 和 S2 均为偶数,请问在这种情况下共有多少种不同的 R1​ 。当 R1​ 或 R2 为空集时我们将 S1 或 S2​ 视为 0。

输入格式

第一行一个整数 T,表示有 T 组数据。

接下来输入 T 组数据,每组数据包含两行:第一行一个整数 N,表示数组 A 的长度;第二行输入 N 个整数从左至右依次为 A0,A1,...,AN−1相邻元素之间用空格分隔。

输出格式

对于每组数据,输出一行,包含一个整数表示答案,答案可能会很大,你需要将答案对 1000000007进行取模后输出。

样例输入

2
2
6 6
2
1 6

样例输出

4
0

样例说明

对于第一组数据,答案为 4。(注意:大括号内的数字表示元素在数组中的下标。)

R1=0,R2=1;此时 S1=A0=6为偶数, S2=A1=6为偶数。

R1=1,R2=0 ;此时 S1=A1=6为偶数, S2=A0=6为偶数。

R1=0,1,R2=;此时 S1=A0+A1=12为偶数, S2=0为偶数。

R1=,R2=0,1;此时 S1=0 为偶数, S2=A0+A1=12为偶数。

对于第二组数据,无论怎么选择,都不满足条件,所以答案为 00。

 


题目的分析:

题目的意思是对于一个集合I,把集合I分成两个子集R1和R2,并且这两个子集中元素的和都是偶数,对于空集,我们认为集合的值为0,子集元素的和为偶数。

要想分为两个偶数,我们先要知道怎么才会产生偶数。

奇数+奇数=偶数、偶数+偶数=偶数、奇数+偶数=奇数

因此,分为下列的两种种情况:

(1)若整个集合I的和为奇数,那么这个集合就无法拆分为两个偶数集合的和,此时只需要输出0就可以。在这个问题中,也就是如果集合中出现的奇数个数为奇数,那么这个集合就无法划分为两个偶数的和

(2)集合I的和为偶数,可能是偶数+偶数产生的,也可能是偶数加奇数产生的,因此我们的选择方式分为两种。一种是选择全部是偶数的子集,另一种是选择偶数个奇数的子集

假设数组A中,偶数元素的个数为even个,奇数元素个数为odd个。

(1)选择的是偶数作为子集,由于偶数+偶数=偶数,因此另一个子集和一定也是偶数。我们需要在even个偶数中选择0,1,2,3,4,……个,这里我们用到了数学中的二项式定理。

二项式定理如下:

公式中的n就是even,我们的取值是0,1,2,3,……,even-1,even。此时我们的a与b都是1,因此二项式系数和=2^(even)。可能的方案数就是   2^(even)。

(2)选择偶数个奇数作为子集,那么我们选择的个数应该是0,2,4,6,……,此时公式应当为二项式系数和的一半(因为我们只选取了偶数个,奇数个我们没有计算,对比全部的项,我们现在是全部项数的一半),因此此时的方案数就是  2^(odd-1)。【这里的odd一定是偶数,因为奇数个奇数我们已经在上面讨论了,结果为0

 总的可能方案数=偶数方案数+偶数个奇数方案数。

代码如下:

#include <iostream>
#include <math.h>
using namespace std;

int main() {

	int T,x,even,odd; //even是偶数,odd是奇数
	long sum; //统计方案数目
	long es=1,os=1;//用来求2^偶数  和  2^(奇数-1)
	int mod = 1000000007;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		cin>>n;
		even = 0;//记录偶数个数,每次使用前初始化为0
		odd = 0;//记录奇数个数,每次使用前初始化为0
		for(int i=0;i<n;i++){
			cin>>x; //输入每个元素,并判断奇偶性
			if(x%2==0)
			{
				even++;
			}
			else
			{
				odd++;
			}
		}
		if(odd%2){    //如果奇数的个数为奇数个,整个集合肯定无法划分为两个偶数子集,输出0
			cout<<0<<'\n';
		}
		else{
			es = 1; os = 1;
			for(int i=0;i<odd-1;i++)
			{
				os = (os*2)%mod;  //求奇数方案
			}
			for(int j=0;j<even;j++)
			{
				es = (es*2)%mod;  //求偶数方案
			}
			sum = (os*es)%mod;  //所有可能的方案数
			cout<< sum<<'\n';
		}
	}	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值