排列

在暴力求解中,常常通过枚举所有的可能排列来得到答案,所有如何生成所有的排列也就十分重要。

生成1~n的排列

采用递归的思想,从前往后逐位进行考虑。

 1 //1~n的全排列(字典序)
 2 void permutation(int n, int *A, int cur)
 3 {
 4     if (cur == n)
 5     {
 6         for (int i = 0; i < n; i++)  printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
 7     }
 8     else for (int i = 1; i <= n; i++) {
 9         int ok = 1;
10         for (int j = 0; j < cur; j++)
11             if (A[j] == i)  ok = 0;
12         if (ok)
13         {
14             A[cur] = i;
15             permutation(n, A, cur + 1);
16         }
17     }
18 }

其实上面这个为了保证字典序,时间复杂度实际变成O(nn)。

可重集的排列

即待排列的数组中有相同的元素,得到所有的排列。

//可重集的排列
//若要字典序,需要保证P有序
void permutation(int n,int *P, int *A, int cur)
{
    if (cur == n)
    {
        for (int i = 0; i < n; i++)  printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
    }
    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[i] == P[j])  c2++;    //可以预处理
        if (c1 < c2)
        {
            A[cur] = P[i];
            permutation(n, P, A, cur + 1);
        }
    }
}

实际上,若不要求字典序,可将时间复杂度降为O(n!)

 1 //打印出a数组的全排列(非字典序)
 2 void permutation(int *a,int n,int cur)
 3 {
 4     if (cur == n)
 5     {
 6         for (int i = 0; i < n; i++)
 7             printf("%d%c", a[i], i == n - 1 ? '\n' : ' ');
 8         return;
 9     }
10     for (int i = 0; i < n - cur; i++) //提取待排数组中的每一个元素
11     {
12         swap(a[cur], a[cur + i]);  //第cur个与cur之后的元素交换
13         permutation(a,n,cur + 1);
14         swap(a[cur], a[cur + i]);  //还原
15     }
16 }

它还有一些好处,不必开辟存储结果的数组,也可用来生成可重集的排列。

下一个排列

枚举所有排列的另一个方法是从字典序最小的排列开始,不停调用“求下一个排列”的过程。C++的STL中提供了一个库函数next_permutation来求下一个排列。

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 10;
 7 int n, a[maxn];
 8 
 9 int main()
10 {
11 
12     scanf("%d", &n);
13     for (int i = 0; i < n; i++)  scanf("%d", &a[i]);
14     sort(a, a + n);          //得到p的最小排列
15     do
16     {
17         for (int i = 0; i < n; i++)  printf("%d%c", a[i], i == n - 1 ? '\n' : ' ');
18     } while (next_permutation(a,a + n));    //求下一个排列
19 
20     return 0;
21 }

 

个性签名:时间会解决一切