多重全排列的生成与构造

Posted wskit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多重全排列的生成与构造相关的知识,希望对你有一定的参考价值。

设有a1+a2+---+aK=N,a1,a2,---,aK为正整数(K>=2),将a[1],a[2],---,a[K]K个数排列至1,2,---N这N个排列位置上,使得a[1],a[2],---,a[K]所占据的排列位置数恰好分别为a1,a2,---,aK,这样占据1,2,---NN个排列位置的a[1],a[2],---,a[K]构成的排列为一个排列位置数为N,排列数数目为K的多重全排列。

现在介绍算法,该算法能够生成符合上述要求的所有多重全排列

说明:以下二种算法仅适用于k>=2的情形,k=1为特殊情形我没有专门处理,不处理问题也不大

算法一(K=3,N=5,a1=2,a2=1,a3=2,a[i]=i):思路简单代码易于理解,代码简单。基本思路为在栈中回溯和向前试探,向前试探寻找满足容量上限的下一个候选数,从而扩大候选解的规模,候选解规模达到N即得到一个多重全排列。无法发现符合容量上限的下一个候选数即回溯

C++代码:

 

 1 #include "stdafx.h"
 2 #include <vector>
 3 #include <iostream>
 4 using namespace std;
 5 
 6 const int K = 3;
 7 const int N = 5;
 8 
 9 int search(int start, const vector<int> &num, const vector<int> &count)  //从start开始顺序查找满足容量限制的第一个数字,查找失败返回0,否则返回找到的数字
10 {
11     for (; start <= K; ++start)
12     {
13         if (num[start - 1] + 1 <= count[start - 1])
14             return start;
15     }
16     return 0;
17 }
18 int main()
19 {
20     vector<int> count{ 2, 1, 2 };  //a1,---,ak值
21     vector<int> num(count.size(), 0); //统计循环过程中1,---,k的数目
22     vector<int> arrange(N, 0);  //存放找到的多重全排列的栈结构
23     int i = 1;   //栈顶指针
24     int number = 0;
25     bool TF = true;  //回溯标志
26 
27     while (true)
28     {
29         if (i == 1)
30         {
31             if (TF == true)
32             {
33                 arrange[i - 1] = 1;
34                 ++num[0];
35                 ++i;
36             }
37             else
38             {   
39                 --num[arrange[i - 1] - 1];
40                 ++arrange[i - 1];
41                 if (arrange[i-1] > K)
42                 {
43                     break;
44                 }
45                 else
46                 {
47                     ++num[arrange[i - 1] - 1];
48                     ++i;
49                     TF = true;
50                 }
51             }
52         }
53         else
54         {
55             if (TF == true)
56             {
57                 arrange[i - 1] = search(1, num, count);
58                 ++num[arrange[i - 1] - 1];
59             }
60             else
61             {
62                 int temp;
63                 if ((temp = search(arrange[i - 1] + 1, num, count)) == 0)
64                 {
65                     --num[arrange[i - 1] - 1];
66                     --i;
67                     continue;
68                 }
69                 else
70                 {
71                     --num[arrange[i - 1] - 1];
72                     arrange[i - 1] = temp;
73                     ++num[arrange[i - 1] - 1];
74                 }
75             }
76 
77             if (i == N)   //找到一个多重全排列输出
78             {
79                 ++number;
80                 cout << "" << number << "个多重全排列:";
81                 for (const int &m : arrange)
82                 {
83                     cout << m;
84                 }
85                 cout << endl;
86                 cout << endl;
87                 TF = false;
88             }
89             else
90             {
91                 ++i;
92                 TF = true;
93             }
94         }
95     }
96     return 0;
97 }

 

算法二(K=3,N=5,a1=2,a2=1,a3=2,a[i]=i):代码相对而言更复杂,方法较笨,就是单纯地模拟手工寻找多重全排列的过程,该过程中按a[1],---,a[k]的顺序逐一选择子排列,成功找到子排列则向前试扩大候选解的规模探寻找下一个,当a[1],---,a[k]的所有子排列全部找到后即获得一个多重全排列,当已无新的子排列可供选择时即回溯

C++代码:

 

  1 #include "stdafx.h"
  2 #include <vector>
  3 #include <iostream>
  4 using namespace std;
  5 
  6 bool find(bool TF, int n, int k, vector<int> &temp)  //TF==true,函数从头寻找满足1<=a1<--<ak<=n的第一个排列a1,a2,--,ak存放在temp中
  7 {                                                    //TF==false,函数寻找上一次找到的存放在temp中满足1<=a1<--<ak<=n的排列a1,a2,--,ak的下一个排列,找到存放在temp中返回true,找不到返回false
  8     int i;
  9 
 10     if (TF == true)
 11     {
 12         i = 0;
 13     }
 14     else
 15     {
 16         i = k - 1;
 17     }
 18 
 19     while (1)
 20     {
 21         if (i == 0)
 22         {
 23             if (TF == true)
 24             {
 25                 temp[i] = 1;
 26             }
 27             else
 28             {
 29                 temp[i]++;
 30             }
 31 
 32             if (temp[i] > n - k + 1)
 33                 return false;
 34 
 35             if (k == 1)
 36                 return true;
 37             i++;
 38             TF = true;
 39         }
 40         else
 41         {
 42             if (TF == true)
 43                 temp[i] = temp[i - 1] + 1;
 44             else
 45             {
 46                 temp[i]++;
 47                 if ((temp[i] - temp[i - 1])>(n - temp[i - 1]) - (k - i) + 1)
 48                 {
 49                     i--;
 50                     TF = false;
 51                     continue;
 52                 }
 53             }
 54 
 55             if (i == k - 1)
 56             {
 57                 return true;
 58             }
 59             i++;
 60             TF = true;
 61         }
 62     }
 63 }
 64 
 65 const int K = 3;
 66 const int N = 5;
 67 
 68 struct back
 69 {
 70     vector<int> sub; //存放find函数找到的多重全排列中的一个子排列的排列位置
 71     vector<int> father;   //存放多重全排列所有N个排列位置被与其对应的sub子排列之前的所有排列占据后剩下的排列位置
 72     back(int k) :sub(k, 0) {}
 73 };
 74 
 75 void diffset(vector<back> &stack, const vector<int> &count)  //计算father和sub的差集,得到sub的下一排列可取得排列位置
 76 {
 77     int i = 0;
 78     int j = 0;
 79     back temp(count[stack.size()]);
 80     while (i < stack.back().father.size() && j < stack.back().sub.size())
 81     {
 82         if (i + 1 < stack.back().sub[j])
 83         {
 84             temp.father.push_back(stack.back().father[i]);
 85             ++i;
 86         }
 87         else if (i + 1 > stack.back().sub[j])
 88         {
 89             ++j;
 90         }
 91         else
 92         {
 93             ++i;
 94             ++j;
 95         }
 96     }
 97 
 98     while (i < stack.back().father.size())
 99     {
100         temp.father.push_back(stack.back().father[i]);
101         ++i;
102     }
103 
104     stack.push_back(temp);
105 
106 }
107 
108 int main()
109 {
110     vector<int> count{2, 1, 2};  //a1,---,ak值
111     int i = 1;  //循环当前正在处理的子排列序号
112     int num = 0;  //多重全排列计数
113     bool TF = true;   //回溯标志,true前进至本层,false回溯至本层
114     vector<back> stack;
115     while (true)
116     {
117         if (i == 1)
118         {
119             if (TF == true)
120             {
121                 stack.push_back(back(count[0]));
122                 find(true, N, count[i - 1], stack.back().sub);
123 
124                 for (int j = 1; j <= N; j++)
125                     stack.back().father.push_back(j);
126                 ++i;
127             }
128             else
129             {
130                 if (find(false, N, count[i-1], stack.back().sub) == true)
131                 {
132                     ++i;
133                     TF = true;
134                 }
135                 else
136                 {
137                     break;
138                 }
139             }
140         }
141         else
142         {
143             if (TF == true)
144             {
145                 diffset(stack, count);
146                 find(true, stack.back().father.size(), count[i - 1], stack.back().sub);
147             }
148             else
149             {
150                 if (find(false, stack.back().father.size(), count[i - 1], stack.back().sub) == false)
151                 {
152                     stack.pop_back();
153                     --i;
154                     continue;
155                 }
156             }
157 
158             if (i == K)   //找到一个多重全排列,输出
159             {
160                 ++num;
161                 vector<int> temp(N, 0);
162                 for (vector<back>::size_type i = 0; i < stack.size(); ++i)
163                 {
164                     for (vector<int>::size_type j = 0; j < stack[i].sub.size(); ++j)
165                     {
166                         temp[stack[i].father[stack[i].sub[j] - 1] - 1] = i + 1;
167                     }
168                 }
169 
170                 cout << "" << num << "个多重排列:";
171                 for (const int &m : temp)
172                 {
173                     cout << m;
174                 }
175                 cout << endl;
176                 cout << endl;
177                 TF = false;
178             }
179             else
180             {
181                 ++i;
182                 TF = true;
183             }
184         }
185     }
186 
187     return 0;
188 }

 

以上代码运行结果均为:

技术分享图片

 

以上是关于多重全排列的生成与构造的主要内容,如果未能解决你的问题,请参考以下文章

hdu5651 xiaoxin juju needs help (多重集的全排列+逆元)

全排列生成器

是否有一种算法可以生成多重集的所有唯一循环排列?

C语言数字全排列的问题(急!!)求C代码和算法

Codeforces Round #723 (Div. 2) D. Kill Anton (字符串构造,全排列)

作业2.有重复全排列和无重复全排列的区别