多重全排列的生成与构造
设有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 }
以上代码运行结果均为: