枚举排列总的有两种方法:递归和求下一个排列
递归法
1~n的排列
利用递归来实现,思路是先输出1开头的排列,再输出2开头的排列,一直到输出n开头的排列,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream>
using namespace std;
void print_permutation(int n,int * A,int cur);
int A[5];
int main() { print_permutation(8,A,0); return 0; }
void print_permutation(int n,int* A,int cur) { int i,ok;
if(cur==n){ for(i=0;i<cur;i++) cout<<A[i]; cout<<endl; return; } for(i=1;i<=n;i++){ ok=1; for(int j=0;j<cur;j++){ if(A[j]==i) ok=0; } if(ok==1){ A[cur]=i; print_permutation(n,A,cur+1); } } }
|
但是这样对每个i都要遍历整个A数组,效率比较低,我们可以先用一个数组把当前A已经有的i存起来,对每个i只用判断一下就ok了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void print_permutation(int n,int* A,int cur) { int i; int memo[13]={0};
if(cur==n){ for(i=0;i<cur;i++) cout<<A[i]; cout<<endl; return; } for(i=0;i<cur;i++) memo[A[i]]++; for(i=1;i<=n;i++){ if(memo[i]==0){ A[cur]=i; print_permutation(n,A,cur+1); } } }
|
生成可重排的数列
如果把问题改成输出数组P元素的全排列,上面的方法就有问题了,因为1~n是没有重复的元素的,可是数组P却是可能存在相同的数的。
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream>
using namespace std;
void print_permutation(int n,int p[], int* A, int cur);
int A[5];
int main() { int p[3]={1,1,1}; print_permutation(3,p,A,0); return 0; }
void print_permutation(int n, int p[],int* A, int cur) { int i; if(cur==n){ for(i=0;i<n;i++) cout<<A[i]; cout<<endl; return; } else{ for(i=0;i<n;i++) {
if (!i || p[i] != p[i - 1]) { int c1 = 0, c2 = 0; for (int j = 0; j < cur; j++) if (A[j] ==p[i]) c1++; for (int j = 0; j < n; j++) if (p[i] == p[j]) c2++; if (c1 < c2) { A[cur] = p[i]; print_permutation(n, p, A, cur + 1); } } } } }
|
下一个排列
枚举排列还可以利用next_permutation函数来实现,其原理是不断的“求下一个排列”,贴上刘汝佳紫书上的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<cstdio> #include<algorithm> //包含next_permutation using namespace std; int main( ) { int n, p[10]; scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &p[i]); sort(p, p+n);
do { for(int i = 0; i < n; i++) printf("%d ", p[i]);
printf("\n"); } while(next_permutation(p, p+n));
return 0; }
|
可重集也是ok的