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

文章讨论了计算给定序列子集异或和的方法,涉及递归策略,以及全排列价值的求解,通过固定元素顺序简化问题。

A

题目描述

现有一个有nnn个元素的序列a=[a1,a2,⋯ ,an]a=[a_1,a_2,\cdots, a_n]a=[a1,a2,,an],定义这个序列的价值为∑i=1ni×ai\sum_{i=1}^{n}i\times a_ii=1ni×ai。空序列的价值为000

先给你一个长度为nnn的序列aaa,求aaa中所有子集价值的异或和,要求子集中元素的相对位置保持不变。

输入

第一行一个整数nnn

接下来一行有n个非负整数表示a1,a2,⋯ ,ana_1, a_2, \cdots, a_na1,a2,,an

输出

一个整数表示所有子集的异或和

样例1

输入

2
1 2

输出

6

数据规模

1≤n≤201\leq n\leq201n201≤a≤1051\leq a\leq10^51a105

解题思路

本题需要明确有哪些序列,如何求得序列价值,以及如何存储价值并异或得到异或和。
由于不同元素的相对位置是固定的,因此可以说一个序列是另一个序列“派生”来的。示例如下:有序列[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], 其价值为value0=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]其价值为value1=1×1+2×2+3×3+4×5=value0+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=[a1,a2,⋯ ,an]a = [a_{1}, a_{2}, \cdots , a_{n}]a=[a1,a2,,an],定义其价值为 ∑i=1nai⊕i\sum_{i=1}^{n}a_{i} \oplus ii=1naii
给出这样一个序列,求其所有排列的价值 viv_{i}vi 的或 v1∣v2∣⋯∣vn−1∣vnv_{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、付费专栏及课程。

余额充值