• 7.2 枚举排列


    7.2.1 生成1~n的全排列

    #include<stdio.h>
    int A[100];
    
    // 输出1~n的全排列
    void print_permutation(int n, int* A, int cur) {
      int i, j;
      if(cur == n) { // 递归边界
        for(i = 0; i < n; i++) printf("%d ", A[i]);
        printf("
    ");
      } else for(i = 1; i <= n; i++) { // 尝试在A[cur]中填各种整数i
        int ok = 1;
        for(j = 0; j < cur; j++)
          if(A[j] == i) ok = 0; // 如果i已经在A[0]~A[cur-1]出现过,则不能再选
        if(ok) {
          A[cur] = i;
          print_permutation(n, A, cur+1); // 递归调用
        }
      }
    }
    
    int main() {
      print_permutation(4, A, 0);
      return 0;
    }

     

    生成0至 n-1的排列:

    bool used[MAX_N]; 
    int perm[MAX_N]; 
    // 生成{0,1,2,3,4,...,n-1}的n!种排列
    void permutation1(int pos, int n) { 
    if (pos == n) { 
    /* 
    * 这里编写需要对perm进行的操作
    */ 
    return ; 
    } 
    // 针对perm的第pos个位置,究竟使用0~n-1中的哪一个进行循环
    for (int i = 0; i < n; i++) { 
    if (!used[i]) { 
    perm[pos] = i; 
    // i已经被使用了,所以把标志设置为true 
    used[i] = true; 
    permutation1(pos + 1, n); 
    // 返回之后把标志复位
    used[i] = false; 
    } 
    } 
    return ; 
    }

     

    7.2.2 生成可重集的排列

    思路:

    1、统计已经输出的某个数的次数,如果少于这个数总共出现的次数,就可以选择这个数

    2、由于P[i]数据相同,所以可能多次递归相同数据,所以需要把P[i]排序,并且在递归前判断是否和前面的数据P[i-1]是否相同。

    #include<cstdio> 
    #include<cstring>
    #include<iostream>
    #include<string>
    #include<algorithm>
    using namespace std;
            
    const int N=20;
    int P[N];   
    int A[N];   
                
    void perm(int n, int *A, int cur)
    {   
        if(n==cur)
        {
            for(int i=0;i<n;i++)
                printf("%d ", A[i]);
            printf("
    ");
        }
        else for(int i=0;i<n;i++)
        {
            int ok=1;
            for(int j=0;j<cur;j++)
            {
                if(A[j]==P[i])
                {
                    ok=0;
                    break;
                }
            }
            if(ok)
            {
                A[cur]=P[i];
                perm(n, A, cur+1);
            }
        }
    }
    
    //生成可重集的排列(错误的)
    void perm2(int n, int *A, int cur)
    {
        if(n==cur)
        {
            for(int i=0;i<n;i++)
                printf("%d ", A[i]);
            printf("
    ");
        }
        else for(int i=0;i<n;i++)
        {
            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)
            {
                //printf("%d %d
    ", c1, c2);
                A[cur]=P[i];
                perm2(n, A, cur+1);
            }
        }
    }
    
    //生成可重集的排列(修正后)
    // P 需要排序过
    // 输出数组P中元素的全排列。数组P中可能有重复元素
    void perm3(int n, int *A, int cur)
    {
        if(n==cur)
        {
            for(int i=0;i<n;i++)
                printf("%d ", A[i]);
            printf("
    ");
        }
        else for(int 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[j]==P[i]) c2++;
            if(c1<c2)
            {
                //printf("%d %d
    ", c1, c2);
                A[cur]=P[i];
                perm3(n, A, cur+1);
            }
        }
    }
    int main()
    {
        int n;
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>P[i];
    
        printf("perm
    ");
        perm(n, A, 0);
    
        printf("perm2
    ");
        perm2(n, A, 0);
    
        printf("perm3
    ");
        perm3(n, A, 0);
    
        return 0;
    }

     

    7.2.3 解答树

    一次生成一个解答元素

    每个叶子对应一个排列,共有n!个叶子。 共有e*n!个节点。

    最后一层叶子n!个,倒数第二层也是n!个,所以上面各层总共加起来也不到n!个。

    就是排列树啦!!

    image

     

    IMG_20140419_125814

    3个元素情况如上,叶子有3!=6, 倒数第二层也是3!=6个。

    7.2.4 下一个排列

    用stl的next_permutation

    注意,return false时有回到最小序列

     

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    /*
     函数实现原理如下:
        在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。
     */
    bool next_perm(int *first, int *last)
    {
        //空区间
        if(first==last) return false;
        int *i=first;
        //只有一个
        if(++i==last) return false;
        i=last; --i;
        while(1) {
            int *ii=i;
            --i;
            //锁定两个相邻元素
            if(*i<*ii) {//如果前一个比后一个小
                int *j=last;
                while(!(*i<*--j));//从尾向前找,直到找到一个比*i大的元素
                printf("swap %d %d
    ", *i, *j);
                iter_swap(i, j);//交换i,j
                reverse(ii, last);//将从ii开始之后的元素逆序重排
                return true;
            }
            if(i==first)//到最前面了,没有更大的序列了
            {
                reverse(first, last);//全部逆序排列,变成最小的序列
                return false;
            }
        }
    }
    
    int main() {
      int n, p[10];
      scanf("%d", &n);
      for(int i = 0; i < n; i++) scanf("%d", &p[i]);
      sort(p, p+n); // 排序,得到p的最小排列
      do {
        for(int i = 0; i < n; i++) printf("%d ", p[i]); // 输出排列p
        printf("
    ");
      //} while(next_permutation(p, p+n)); // 求下一个排列
      } while(next_perm(p, p+n)); // 求下一个排列
    //最后会输出最小序列
        for(int i = 0; i < n; i++) printf("%d ", p[i]); // 输出排列p
      return 0;
    }
  • 相关阅读:
    GDOI 2019 退役记
    SHOI2019 游记
    【WC2014】紫荆花之恋
    PKUWC 2019 & NOIWC2019 比赛总结 Formal Version
    WC 2019 颓废记
    VDUVyRLYJC
    Git学习
    DOM学习笔记
    python基础---->AJAX的学习
    python基础---->进程、线程及相关等
  • 原文地址:https://www.cnblogs.com/cute/p/3668923.html
Copyright © 2020-2023  润新知