暴力求解法

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);
                }
            }
        }
    }
}

你可能感兴趣的:(知识点积累)