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;
}
原文地址:https://www.cnblogs.com/cute/p/3668923.html