康託展開是一個全排列到一個天然數的雙射。設有n個數(1,2,3,4,…,n),能夠有組成不一樣(n!種)的排列組合,康託展開表示的就是是當前排列組合在n個不一樣元素的全排列中的名次。c++
\(X = a[n] * (n - 1)! + a[n - 1] * (n - 2)! + a[n - 2] * (n - 3)! …… + a[2] * 1! + a[1] * 0!\)app
例如3的全排列優化
估計你們應該不用解釋都應該明白了,這裏的a[i]係數是如何來的了spa
3 > 2, 3 > 1,獲得a[3] = 2,2 > 1,獲得a[2] = 1
其中的康託展開的數值表明的是當前項在全排類中的數值下標(注意下標從0開始計數)code
/* Code by lifehappy 2020:04:21 康託展開計算O(n * n) 未優化,明天把優化的代碼補上。 */ #include<bits/stdc++.h> using namespace std; int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//階乘 int cantor(int a[], int n) { int s = 0; for(int i = 0; i < n; i++) { int num = 0; for(int j = i + 1; j < n; j++) if(a[i] > a[j]) num++; s += num * fac[n - i - 1]; } return s; } int main() { int a[5] = {4, 5, 3, 1, 2}, n = 5; printf("%d\n", cantor(a, n)); return 0; }
94
也就是康託展開的逆過程,就拿上面的例子來講it
排列4 5 3 1 2的康託展開值是94。class
/* Code by lifehappy 2020:04:21 逆康託展開計算O(n * n) 康託展開項未優化 */ #include<bits/stdc++.h> using namespace std; int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//階乘 int ans[10]; int cantor(int a[], int n) { int s = 0; for(int i = 0; i < n; i++) { int num = 0; for(int j = i + 1; j < n; j++) if(a[i] > a[j]) num++; s += (num * fac[n - i - 1]); } return s; } void decantor(int s, int n) { vector<int> a; for(int i = 1; i <= n; i++) a.push_back(i); for(int i = 4; i >= 0; i--) { int pos = s / fac[i]; s %= fac[i]; ans[n - i - 1] = a[pos]; a.erase(a.begin() + pos); } } int main() { int a[5] = {4, 5, 3, 1, 2}, n = 5; printf("%d\n", cantor(a, n)); decantor(cantor(a, n), n); for(int i = 0; i < n; i++) printf("%d%c", ans[i], i + 1 == n ? '\n' : ' '); return 0; }