题目描述
现有一个有 n n n个元素的序列 a = [ a 1 , a 2 , ⋯ , a n ] a=[a_1,a_2,\cdots, a_n] a=[a1,a2,⋯,an],定义这个序列的价值为 ∑ i = 1 n i × a i \sum_{i=1}^{n}i\times a_i ∑i=1ni×ai。空序列的价值为 0 0 0。
先给你一个长度为 n n n的序列 a a a,求 a a a中所有子集价值的异或和,要求子集中元素的相对位置保持不变。
异或和: 位运算的一种。如果a、b两个值不相同,则异或结果为 1 1 1;如果a、b两个值相同,异或结果为 0 0 0。
输入输出格式
输入
第一行一个整数 n n n
接下来一行有n个非负整数表示 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an
输出
一个整数表示所有子集的异或和
样例1
输入
2
1 2
输出
6
数据规模
1 ≤ n ≤ 20 1\leq n\leq20 1≤n≤20, 1 ≤ a ≤ 1 0 5 1\leq a\leq10^5 1≤a≤105
提示
样例1中共有四个子集 [ ] , [ 1 ] , [ 2 ] , [ 1 , 2 ] [],[1],[2],[1,2] [],[1],[2],[1,2],他们的价值分别为0,1,2,5,其异或和 0 ⨁ 1 ⨁ 2 ⨁ 5 = 6 0\bigoplus1\bigoplus2\bigoplus5=6 0⨁1⨁2⨁5=6
Source
Provided by hrz
思路分析
核心思路:用递归求出集合的子集,并有所求自己求出子集价值以及异或和。
- 求子集: 每一个元素有两种选择:取、不取
核心代码:
tag[start] = 1;
subArray(array,start + 1,n);//该元素选入,向下递归
tag[start] = 0;
subArray(array,start + 1,n);//该元素不选,向下递归
关键:开bool数组,给每个元素打上标签,用来表示选入或没选入,直到start==n,递归到最后,所有子集生成。
- 使用 ^= 运算符来累积异或和。
整体思路:
(1) 生成序列的所有子集:每个元素有两种情况,被选入子集中或没被选入。用一个bool数组对元素是否被选入进行标记,接着通过向下递归来处理下一个位置。
(2) 当递归到达序列的末尾,将选入子集的元素保存,来计算子集的价值。
(3) 利用^运算符对子集的价值进行异或操作,最终返回异或和
AC代码
#include<iostream>
using namespace std;
int subset[1000]={};//存子集
bool tag[100];//标记元素
int value=0;//求异或和
int count(int *a,int start,int n){
int sum=0,num=0;//求子集价值,标记位置
if(start<n){
tag[start]=1;
count(a,start+1,n);//该元素选入,向下递归
tag[start]=0;
count(a,start+1,n);//不选
}
if(start==n){
for(int i=0;i<n;i++){
if(tag[i]){
subset[num]=a[i];//如果这个元素存在,生成子集并保存
num++;
}
}
for(int j=0;j<num;j++){
sum+=(j+1)*subset[j];//计算子集价值
}
value^=sum;//计算异或和
}
return value;
}
int main(){
int a[20],n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
cout<<count(a,0,n);
return 0;
}
改进后的代码
#include<iostream>
using namespace std;
class Solution{
public:
int count(int *a,int start,int n);
~Solution();//析构函数,用于释放动态分配的内存
int* subset;//存子集
bool* tag;//标记元素
int value=0;//求异或和
};
int Solution::count(int *a,int start,int n){
int sum=0,num=0;//求子集价值,标记位置
if(start<n){
tag[start]=1;
count(a,start+1,n);//该元素选入,向下递归
tag[start]=0;
count(a,start+1,n);//不选
}
if(start==n){
for(int i=0;i<n;i++){
if(tag[i]){
subset[num]=a[i];//如果这个元素存在,生成子集并保存
num++;
}
}
for(int j=0;j<num;j++){
sum+=(j+1)*subset[j];//计算子集价值
}
value^=sum;//计算异或和
}
return value;
}
Solution::~Solution(){
delete[] subset;
delete[] tag;
}
int main(){
int* a;
int n;
cin>>n;
a=new int[20];
for(int i=0;i<n;i++){
cin>>a[i];
}
Solution solution;//创建对象
solution.subset=new int[1000]; // 动态分配subset内存
solution.tag=new bool[100]; // 动态分配tag内存
cout<<solution.count(a,0,n);
delete[] a;
return 0;
}
- 使用动态内存分配
- 进行封装