【数据结构】|实验1_递归练习 A子集价值

题目描述

现有一个有 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 1n20 1 ≤ a ≤ 1 0 5 1\leq a\leq10^5 1a105

提示

样例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 0125=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;
}
  1. 使用动态内存分配
  2. 进行封装
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值