题目
问题描述
小蓝有一个长度为 N 的数组 A = [A0, A1, . . .,AN−1]。现在小蓝想要从 A 对应的数组下标所构成的集合 I = {0, 1, 2, . . . , N − 1} 中找出一个子集 R1,那么 R1在 I 中的补集为 R2。记 S1 = ∑ r ∈ R1 Ar,S2 = ∑ r ∈ R2 Ar,我们要求 S1 和 S2 均为偶数,请问在这种情况下共有多少种不同的 R1。当 R1 或 R2 为空集时我们将S 1 或 S 2 视为 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};此时 S 1 = A0 = 6 为偶数, S 2 = A1 = 6 为偶数。
R1 = {1}, R2 = {0};此时 S 1 = A1 = 6 为偶数, S 2 = A0 = 6 为偶数。
R1 = {0, 1}, R2 = {};此时 S 1 = A0 + A1 = 12 为偶数, S 2 = 0 为偶数。
R1 = {}, R2 = {0, 1};此时 S 1 = 0 为偶数, S 2 = A0 + A1 = 12 为偶数。
对于第二组数据,无论怎么选择,都不满足条件,所以答案为 0。
评测用例规模与约定
对于 20% 的评测用例,1 ≤ N ≤ 10。
对于 40% 的评测用例,1 ≤ N ≤ 102。
对于 100% 的评测用例,1 ≤ T ≤ 10, 1 ≤ N ≤ 103, 0 ≤ Ai ≤ 109。
思路
这题我们使用动态规划dp(动态规划关键在两点;a, 怎么表示状态 b,怎么表示状态之间的关系),解决问题,dp[i][0] 表示前i个元素中子集和为偶数的个数而dp[i][1]表示前i个元素中子集和为奇数的个数(a),从i从1开始遍历arr[i] 是否要加入,当arr[i]为偶数时(因为加不加奇偶性不会发生改变)则dp[i+1][0] = dp[i][0]*2 ,dp[i+1][1] = dp[i][1]*2 , 当arr[i]为奇数(可以改变奇偶性)时dp[i+1][0]和dp[i+1][1]都是dp[i][0]+dp[i][1] ,因为对于dp[i+1][0] dp[i][0]都是偶数表示不加arr[i]时原来的的与dp[i][1]都是奇数表示加 arr[i]变成偶数了,dp[i+1][0] 同理
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
long long arr[N];
long long dp[N][2];//表示前i个数的 dp[i][0]表示子集和为偶数的数量 dp[i][1]表示子集和为奇数的数量
long long t,n;
long long mod = 1e9+7;
int main()
{cin>>t;while(t--){cin>>n;memset(arr,0,sizeof(arr));memset(dp,0,sizeof(dp));long long sum = 0;for(int i = 0;i < n;i++){cin>>arr[i];sum += arr[i];}if(sum %2 == 1){cout<<0<<endl;continue;}else{dp[0][0] = 1,dp[0][1] = 0;for(int i = 0;i < n;i++){if(arr[i] %2 == 0){//arr[i] 是偶数,那么无论我们是否选取它,子集的和的奇偶性都不会改变dp[i+1][0] = dp[i][0]*2%mod;dp[i+1][1] = dp[i][1]*2%mod;}else{//arr[i]是奇数 分为选和不选 选 那么前一个的子集+当前数 的奇偶会发生改变dp[i+1][0] = dp[i][0]+dp[i][1]%mod;dp[i+1][1] = dp[i][0]+dp[i][1]%mod;}}cout<<dp[n][0]%mod<<endl;}}return 0;
}
总结
- 动态规划关键在两点;a, 怎么表示状态 b,怎么表示状态之间的关系
- 注意dp数组要初始化和有的要设置初始值