实验1.2 递归练习——全排列问题(详细图解)
题目描述
现有一个有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=1nai⊕i
给出这样一个序列,求其所有排列的价值
v
i
v_{i}
vi 的或
v
1
∣
v
2
∣
⋯
∣
v
n
−
1
∣
v
n
v_{1}| v_{2} | \cdots | v_{n-1} | v_{n}
v1∣v2∣⋯∣vn−1∣vn
其中
∣
|
∣ 为位运算或操作,
⊕
\oplus
⊕ 为位运算异或操作
题目格式
输入
输入的第一行是一个整数 n (2<=n<=10),表示需排列的数的个数。接下来一行是 n 个整数,数的范围是 0 到 100000,每两个相邻数据间用一个空格分隔。
输出
一个整数,代表所有排列价值的或。
思路与探讨
-
递归算法:把一个大型复杂的问题,一次次通过递归调用而层层转化为一个与原问题相似的规模较小的问题来求解,基本思想是将大问题一步步化为小问题。小破站递归讲解视频分享
-
题解思路:根据递归得出全排列,再根据所求得出价值和求或。
(这不废话嘛-
关于全排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。例:
-
针对数组{1,2,3} 其全排列为: 1,2,3 1,3,2 2,1,3 2,3,1 3,2,1 3,1,2
-
-
求解全排列的思路:在递归的过程中将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列,直到n=0。结合 swap 函数,逐步排列逐步复位。
-
核心代码:
for(int i = start;i < end;i++) { swap(list[i],list[start]); //交换 permutations(list,start + 1,end); //递归回来时还原到本层的初始模样, //以便于下一次i++继续做递归时出发点是一样的。 swap(list[i],list[start]); }
-
终止条件:
if(start == end) { //已处理完一组排列,可求值 for(int i = 0;i < end;i++) { value += list[i] ^ (i+1); //求价值 } result |= value; }
-
-
思路图解(以{1,2,3}为例):
若已看懂思路,试着自己写~
实现代码
#include <iostream>
using namespace std;
int result = 0;//存储排列价值的或
void swap(int *a,int *b)
{//交换函数
int temp = *a;
*a = *b;
*b = temp;
}
int permutations(int list[],int start,int end)
{//生成list[start:end]的所有排列并求值
int value = 0;
if(start == end)
{ //已处理完一组排列,可求值
for(int i = 0;i < end;i++)
{
value += list[i] ^ (i+1); //求价值
}
result |= value;
}
else
{
for(int i = start;i < end;i++)
{
swap(list[i],list[start]); //交换
permutations(list,start + 1,end);
//递归回来时还原到本层的初始模样,
//以便于下一次i++继续做递归时出发点是一样的。
swap(list[i],list[start]);//复位
}
}
return result;
}
int main()
{
int n;
int list[10];
cin >> n;
for(int i = 0;i < n;i++)
{
cin >> list[i];
}
cout << permutations(list,0,n) << endl;
return 0;
}