第一节呢就是枚举排列。
这里的排列指的是一个集合的元素中根据字典序进行排列。用wikioi的一道题来进行解释吧
样例输入 Sample Input
3
样例输出 Sample Output
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
此排列的生成过程可以认为是 第一位从1开始进行, 而第二位从可能的最小的数开始遍历,第三位又是在第二位的基础上从可能的最小的数上继续变化。
这里说白了就是一个递归过程。
1.得到已经排完的序列a,以及cur表示当前进行到的位置(从0开始计数)
2.从此序列中得到可能的数(不重复),从其中进行下一次排列。
void pailie(int* a,int cur) { int i,j,k,ok; //如果cur == n 表面 a已经到了尽头 开始打印序列 (递归边界) if(cur==n) { for(k=0;i<n;i++) printf("%d ",a[i]);//printf 的效率比cout快不少呢 printf(" "); return; } for(i=1;i<=n;i++) { ok=1;//设定标志变量 for(j=0;j<cur;j++) if(a[j]==i) { ok=0; break;//找到了一个还没有在a中出现的最小的元素用来打头 } if(ok) { a[cur]=i;//打头 pailie(a,cur+1);//打头之后后面继续走,此时的已排好的序列中已经包含了i 光标也前进了一位 } } }之后调用pailie(ans,0);即可
这里用递归来处理是非常好的选择,STL库也提供了一个方法,就是用不断根据上一个序列生成下一个序列的思维方式来处理问题
首先我们要获得一个最字典序的排列,用sort函数就可以了
然后用do while循环来调用next_permutation 这样的好处是哪怕只有一个排列也可以处理了、
核心代码如下
#include <algorithm> <span style="white-space:pre"> </span>int p[4]={1,4,2,2}; int n=4; sort(p,p+n);//不知道内部实现到底是什么 = = 凑合用吧stl应该不会坑人 do { for(int i=0;i<n;i++) printf("%d ",p[i]); printf(" "); }while(next_permutation(p,p+n));
下面来说说怎么处理可重复的排列。(书上说next_permutation可以用来处理重复序列可是我发现不好使呀~ 不知道那里设置错了,以后再来解决)
已经解决,原年是对重复排列的理解出现了错误
那么就在递归的方法上进行加工。
in
4
1 2 3 2
out
1 2 2 3
1 2 3 2
1 3 2 2
2 1 2 3
2 1 3 2
2 2 1 3
2 2 3 1
2 3 1 2
2 3 2 1
3 1 2 2
3 2 1 2
3 2 2 1
这里我们就要考虑几个细节问题。
比如 如何区分重复了的元素,使得排列数量保持正确 这就需要我们记录某一种元素在已经拍完的序列A中出现的次数c1,和总集合的中的个数c2
只要c1<c2 说明还有可以排的元素,所以要继续递归。
void pailie(int* a,int cur) { int k; //如果cur == n 表面 a已经到了尽头 if(cur==n) { for(int i=0;i<n;i++) printf("%d ",a[i]); putchar(' '); return; } for(int i=0;i<n;i++)//如果是i=0即第一个元素 则直接进入处理 因为他肯定没有被重复,如果是第2个开始则需要进行筛选 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[j] == p[i]) c2++; if(c1 < c2) { a[cur] = p[i];//将元素充入排列中 pailie2(a, cur+1); } } }
总体上大概就是如此