1.生成1~n的排列
1)题解
\quad循环变量i是当前考察的A[cur]。为了检查元素i是否已经用过,上面的程序用到了一个标志变量ok,初始值为1(真),如果发现有某个元素A[j]=i时,则改为0(假)。如果最终ok仍为1,则说明i没有在序列中出现过,把它添加到序列末尾(A[cur]=i)后递归使用。
\quad声明一个足够大的数组A,然后调用print_permutation(n,A,0),即可按字典序输出1~n的所有排列。
2)代码
void print_permutation(int n,int *A,int cur){
if(cur==n){
for(int i=0;i<n;i++){
printf("%d ",A[i]);
}
printf("\n");
}
else{
for(int i=1;i<=n;i++){
int ok=1;
for(int j=0;j<cur;j++){
if(A[j]==i){
ok=0;
break;
}
}
if(ok){
A[cur]=i;
print_permutation(n,A,cur+1);
}
}
}
}
2.生成可重集的排列
1)题解
需要注意两点
- 取消“禁止A数组中出现重复”,因为P中本来就有重复元素,依然保留这个禁令,明显是错误的。一个解决方案是统计A[0]~A[cur-1]中P[i]的出现次数c2以及数组P中P[i]的出现次数c1,只要c1>c2,就可以继续递归下去。
- 我们枚举的下标i应不重复、不遗漏地取遍所有P[i]。由于P数组已经排序过,所以只需检查P的第一个元素和所有“与前一个元素不相同”的元素。
2)代码
void print_permutation(int n,int *P, int* A, int cur){
if(cur==n){
for(int i=0;i<n;i++){
printf("%d ",A[i]);
}
printf("\n");
}
else{
for(int i=0;i<n;i++){
int c1=0,c2=0;
//当i=0时,P[i]!=P[i-1]不会被执行,所以不会发生数组越界的问题。
if(!i||P[i]!=P[i-1]){
for(int k=0;k<n;k++){
if(P[i]==P[k]){
c1++;
}
}
for(int k=0;k<cur;k++){
if(P[i]==A[k]){
c2++;
}
}
if(c2<c1){
A[cur]=P[i];
print_permutation(n,P,A,cur+1);
}
}
}
}
}