康託展開了解一下—NPC
首先,度娘鎮樓:康託展開是一個全排列到一個自然數的雙射,常用於構建哈希表時的空間壓縮。 康託展開的實質是計算當前排列在所有由小到大全排列中的順序,因此是可逆的。
注:雙射=》既滿足單射(一個變量對應一個函數值)又滿足滿射(對於所有函數值都有一個以上變量與之對應)
公式如下:
其中,ai爲整數,並且。表示的是位於位置i後面的數小於的個數(未出現的數)
舉例:
n = 5 對於45321這個排列數中,有4的後面比4小的數有3、2、1,共3個,5後有3、2、1,3後2、1,2後1,1後面就沒有了。
因此有:3*4!+3*3!+2*2!+1*1!+0*0! = 95
所以比45321小的排列有95個,即45321爲第96個排列;
康託展開的逆運算
既然康託展開是一個雙射,那麼一定可以通過康託展開值求出原排列,即可以求出n的全排列中第x大排列。
對於n = 5給出61可以算出起排列組合爲34152。具體過程如下:
用 61 / 4! = 2餘13,說明a5=2,說明比首位小的數有2個,所以首位爲3。
用 13 / 3! = 2餘1,說明a4=2,說明在第二位之後小於第二位的數有2個,則第二位爲4。
用 1 / 2! = 0餘1,說明a3=0,說明在第三位之後沒有小於第三位的數,則第三位爲1。
用 1 / 1! = 1餘0,說明a2=1,說明在第二位之後小於第四位的數有1個,則第四位爲5。
最後一位自然就是剩下的數2。
通過以上分析,所求排列組合爲 34152
所以康託展開在OI中什麼鬼用法?
一個排列對應一個值且值是連續的自然數 當然壓空間啊 例題:八數碼,魔板
板子代碼:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 int shu[10];//記錄數字排列 6 bool vis[10];//標記是否出現過 7 int jc[10]= {1,1,2,6,24,120,720,5040,40320};//預處理階乘值 8 int KT(int *a,int n){ 9 int num = 0; 10 for(int i = 1;i <= n;i++){ 11 int cnt = 0; 12 for(int j = i+1;j <= n;j++){//統計有多少比第i位小的數 13 if(shu[i] > shu[j]) cnt++; 14 } 15 num += cnt*jc[n-i]; 16 } 17 return num; 18 } 19 void re_KT(int num,int len){ 20 int i,j,t; 21 memset(vis,0,sizeof(vis)); 22 --num; 23 for(i = 0;i < len;i++){ 24 t = num/jc[len-i-1]; 25 for(j = 1;j <= len;j++){ 26 if(!vis[j]){ 27 if(t == 0) break; 28 --t; 29 } 30 } 31 shu[i] = j; 32 vis[j] = true; 33 num %= jc[len-i-1]; 34 } 35 } 36 int main() 37 { 38 int len; 39 scanf("%d",&len); 40 for(int i = 1;i<=len;i++){ 41 scanf("%d",&shu[i]); 42 } 43 int KT_num = KT(shu,len); 44 printf("%d\n",KT_num); 45 re_KT(KT_num+1,len);//康託展開值表示比它的有多少個排列數 46 for(int i = 0;i<len;i++){ 47 printf("%d ",shu[i]); 48 } 49 return 0; 50 }
1 5 2 1 4 3 2 5
1 14 2 1 4 3 2 5