多重全排列的生成與構造
阿新 • • 發佈:2018-06-19
特殊 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 }
以上代碼運行結果均為:
多重全排列的生成與構造