1. 程式人生 > >多重全排列的生成與構造

多重全排列的生成與構造

特殊 namespace amp else 回溯 順序查找 我沒 sea .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 }

以上代碼運行結果均為:

技術分享圖片

多重全排列的生成與構造