参考来源
博客:
https://blog.youkuaiyun.com/qq_43333395/article/details/96397197#commentBox
题目链接
http://codeforces.com/gym/102267/problem/K
题意
给你一个集合,问它的所有子集(不为空)的元素或了之后 的和是多少。
理解
既然要求所有的子集的异或和,而这里n值又很小,O(2n)不会超时,所以直接遍历所有的子集,取或,求和,即可得到答案。
状态压缩挺暴力的,数据量比较大的时候,就要根据题意,排除不合法的方案,使一行的总方案数大大减少从而减少枚举。
一些操作
以下代码能够输出它的所有子集,如果不理解可以用这输出看一下。
外层循环 i 的值为 1 到 2n ,代表了所有取的情况(不取零个),从取一个元素 到 取所有元素,并且每一种取法(取x个)对应的所有方案都存在,做到不重不漏。
内层循环 j 遍历一遍数组,用 i & (1 << j ) 判断要取哪几个
for (int i = 1; i < (1 << n); i++)
{
for (int j = 0; j < n; j++)
if (i & (1 << j))
{
cout<<a[j]<<" ";
//cout<<"i:"<<i<<" j:"<<j;
}
cout<<endl;
}
代码
较慢的代码,但挺好理解
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll a[25];
ll cnt, num = 0;//这里要记得long long
void solve(int n)
{
for (int i = 0; i < (1 << n); i++)
{
cnt = 0;
for (int j = 0; j < n; j++)
if (i & (1 << j))
cnt = cnt | a[j];//某个子集内的所有元素取或
num += cnt;//求和
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
solve(n);
printf("%lld\n", num);
return 0;
}
另一份代码,用了更多的位运算,速度上会更快。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[25];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0;i < n;i++)
{
scanf("%d", &a[i]);
}
int all = (1<<n);//2^n
int pos, t, tt;
long long res = 0;
for(int i = 1 ;i < all;i++)//遍历所有情况
{
pos = 0, t = 0, tt = i;
while(tt)
{
if(tt & 1)
t |= a[pos];
tt >>= 1;
pos++;
}
res += t;
}
printf("%lld\n", res);
return 0;
}