零、全排列
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
一、递归方法(非字典序)(dfs)
当我们要统计全排列的情况时,最直接的就是枚举,数一数,为了高效的计数,比如123的全排列,我们通常先固定第一个位置上的数“1”,然后后面可以是2,3或者3,2;然后再固定2,数1,3、3,1;最后3,1,2和3,2,1。
代码示例:
//递归全排列
#include<bits/stdc++.h>
using namespace std;
int n;
int arr[50];
long long num;
void full_permutation(int first)
{
if(first==n){
num++;
//for(int i=1;i<n;++i) cout<<arr[i]<<' ';
//cout<<arr[n]<<endl;
return ;
}
for(int i=first;i<=n;++i){
swap(arr[first],arr[i]);
full_permutation(first+1);
swap(arr[first],arr[i]);
}
}
int main()
{
ios::sync_with_stdio(false);
//1~n
cin>>n;
num=0;
for(int i=1;i<=n;++i) arr[i]=i;
full_permutation(1);
cout<<num<<"个"<<endl;
return 0;
}
二、STL方法(字典序)
关于next_permutation和prev_permutation
在STL中next_permutation和prev_permutation是和排列相关的函数,前者是求出下一个排列组合,而后者是求出上一个排列组合。所谓上一个、下一个就是按照字典序排序的上一种、下一种排列,比如一个排列是acbd 那么下一个排列就是 acdb 上一个就是abdc。而abcd没有上一个排列,dcba没有下一个排列。
函数实现的原理就是(next_permutation)在当前排列下,从右端找到一个元素*p,满足*p<*(p+1),那么遍历*p之后所有的元素,找到大于*p的最小元素*(p+n),将*p和*(p+n)替换内容,最后将*p之后的所有元素进行倒叙,得到的排列就是“下一个”排列。例如:对于127384这个排列,从右往左看,8>4不满足*p<*(p+1),而3<8,满足,这时候3右边的元素有8和4,4是大于3的最小元素,将3和4替换得到127483,最后4后面的元素倒序,得到127438,即为127384的下一个排列。 对于prev_permutation,操作的思想类似,先从右边找到一个数满足这个数大于右边紧邻的数,然后在它右边找到比它小的最大数,再替换倒叙即可。
于是利用next_permutation,就能方便的进行字典序全排列了。只需要对初始序列排下序,再循环调用该函数,初始的排序不要忘记了,因为是全排列。
另外STL中next_permutation的函数原型为:
template<class BidirectionalIterator>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last
);
template<class BidirectionalIterator, class BinaryPredicate>
bool next_permutation(
BidirectionalIterator _First,
BidirectionalIterator _Last,
BinaryPredicate _Comp
);
两个重载函数,第二个带谓词参数_Comp,其中只带两个参数的版本,默认谓词函数为"小于".
返回值:bool类型。对于当前的地址内的序列如果不是最大字典序,进行下一个的排列,成功返回true,否则返回false。注意返回false同时会重新设置该排列为最小字典序。
#include<bits/stdc++.h>
using namespace std;
const int maxn=50;
vector<int> arr(maxn);
int main()
{
ios::sync_with_stdio(false);
int n;
int num=1;
cin>>n;
for(int i=1;i<=n;++i) arr[i]=i;
sort(arr.begin()+1,arr.begin()+n+1);
for(int i=1;i<=n;++i) cout<<arr[i]<<' ';
cout<<endl;
while(next_permutation(arr.begin()+1,arr.begin()+n+1))
{
num++;
for(int i=1;i<=n;++i) cout<<arr[i]<<' ';
cout<<endl;
}
cout<<num<<"个"<<endl;
return 0;
}