SDUCS数据结构实验一——递归练习

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中所有子集价值的异或和,要求子集中元素的相对位置保持不变。

输入

第一行一个整数 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 , 2 , 3 , 4 , 5 , 6 ] [1,2,3,4,5,6] [1,2,3,4,5,6],我们选取一个子集 [ 1 , 2 , 3 ] [1,2,3] [1,2,3], 其价值为 v a l u e 0 = 1 × 1 + 2 × 2 + 3 × 3 = 14. value_0=1\times1+2\times2+3\times3=14. value0=1×1+2×2+3×3=14. 同时我们可以选取另一个子集 [ 1 , 2 , 3 , 5 ] [1,2,3,5] [1,2,3,5]其价值为 v a l u e 1 = 1 × 1 + 2 × 2 + 3 × 3 + 4 × 5 = v a l u e 0 + 4 × 5. value_1=1\times1+2\times2+3\times3+4\times5=value_0+4\times5. value1=1×1+2×2+3×3+4×5=value0+4×5.因此我们可以不用重复从头计算派生子集的价值,而是直接从原子集的基础上加上一个值得到派生子集的价值。这样可以降低算法的时间复杂度。当然,本题的数据并没有限制算法的时间复杂度,如果直接计算每一个子集的价值然后异或也是完全可以AC的。

代码(此代码已使用,请勿直接抄袭)

#include<iostream>
using namespace std;
#define ll long long
ll SUM = 0;
void dg_value(int loc,ll a[],int deep, int n,ll save_value) {
	if (deep > n||n-loc==0)return;
	for (int i = loc; i < n; i++) {
		save_value += a[i]*deep;
		SUM = SUM ^ save_value;
		dg_value(i+1,a, deep + 1, n, save_value);
		save_value -= a[i] * deep;
	}
}
int main() {
	int n;
	cin >> n;
	ll a[22];
	for (int i = 0; i < n; i++)cin >> a[i];
	dg_value(0,a, 1, n, 0);
	cout << SUM;
}

B

题目描述

现有一个有n 个元素的序列 a = [ a 1 , a 2 , ⋯   , a n ] a = [a_{1}, a_{2}, \cdots , a_{n}] a=[a1,a2,,an],定义其价值为 ∑ i = 1 n a i ⊕ i \sum_{i=1}^{n}a_{i} \oplus i i=1naii
给出这样一个序列,求其所有排列的价值 v i v_{i} vi 的或 v 1 ∣ v 2 ∣ ⋯ ∣ v n − 1 ∣ v n v_{1}| v_{2} | \cdots | v_{n-1} | v_{n} v1v2vn1vn
其中 ∣ | 为位运算或操作, ⊕ \oplus 为位运算异或操作

输入

输入的第一行是一个整数 n (2<=n<=10),表示需排列的数的个数。接下来一行是 n 个整数,数的范围是 0 到 100000,每两个相邻数据间用一个空格分隔。

输出

一个整数,代表所有排列价值的或。

样例1

输入

3
1 2 3

输出

6

解题思路

本题的关键在于求得全排列。为了更好的帮助读者理解求全排列算法的思路,我们由易到难列举如下几个例子:

  1. 对于序列 [ 1 , 2 ] [1,2] [1,2],固定1为第一位,有序列 [ 1 , 2 ] [1,2] [1,2];固定2为第一位,有序列 [ 2 , 1 ] [2,1] [2,1]。等同于把1和2进行交换。
  2. 对于序列 [ 1 , 2 , 3 ] [1,2,3] [1,2,3],固定1为第一位,有序列 [ 1 , 2 , 3 ] [1,2,3] [1,2,3], [ 1 , 3 , 2 ] [1,3,2] [1,3,2];固定2为第一位,有序列 [ 2 , 1 , 3 ] [2,1,3] [2,1,3], [ 2 , 3 , 1 ] [2,3,1] [2,3,1];固定3为第一位,有序列 [ 3 , 2 , 1 ] [3,2,1] [3,2,1], [ 3 , 1 , 2 ] [3,1,2] [3,1,2]。当固定第一位时,相当于只用考虑除第一位外的剩余两位。此时问题转化为序列中有两个元素的问题。
  3. 对于序列 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4],还是分别固定1,2,3,4为第一位,剩余的三位元素按照上面“2.”的方式处理。

因此,对于递归的每一层,我们只需要分别让序列的每一位和第一位交换(此处“每一位”包含第一位元素),得到的序列就可以不再考虑第一位元素,而是只考虑后面的序列。而后面的序列仍可以按照刚才的方式处理。直到剩下的数组只有一个元素的时候,我们就得到了一个排列结果,这也就是递归返回的条件。按照这种方式就可以求得所有的排列。

代码(此代码已使用,请勿直接抄袭)

#include<iostream>
using namespace std;
#define ll long long
ll result = 0;
ll value(int a[],int n) {
	ll tmp = 0;
	for (int i = 0; i < n; i++) {
		tmp += a[i]^(i+1);
	}
	return tmp;
}

template<class T>
void sweap(T& a,T& b) {
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

void dg(int a[], int n,int deep) {
	if (deep == n) {
		result |= value(a, n);
	}
	for (int i = deep; i < n; i++) {
		sweap(a[i], a[deep]);
		dg(a, n, deep + 1);
		sweap(a[i], a[deep]);
	}
	return;
}

int main() {
	int a[100];
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> a[i];
	dg(a, n, 0);
	cout << result;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值