1. 程式人生 > >【哈希!簡】康托展開與逆康托展開

【哈希!簡】康托展開與逆康托展開

[] return 動態 bits n-1 階乘 clu 排列 oid

康托展開是利用全排列與當前排列次序的映射建立一個簡易哈希表

康托展開

ans=a0*(n-1)!+a1*(n-2)!+····+an*(n-n)!

找了半天解釋,

就是ai表示剩下的數字中小於當前該數的個數,然後乘以剩下的數字的階乘

意思也就說,剩下的數字中小於當前該數都可以代替當前數字,乘以階乘就是剩下數字的全排列

比如CBAD,BA可以代替C。同時剩下三個數字也可以作為全排列

而逆康拓展開就是

通過計算當前位置外剩余的位置的數目,計算該階乘,然後用給予的數字除以該數字,計算出來的除數就是比該數字小的個數,該數字變成余數(輾轉相除)

在(1,2,3,4,5) 給出61可以算出起排列組合為34152
具體過程如下:
用 61 / 4! = 2余13,說明 ,說明比首位小的數有2個,所以首位為3。
用 13 / 3! = 2余1,說明 ,說明在第二位之後小於第二位的數有2個,所以第二位為4。
用 1 / 2! = 0余1,說明 ,說明在第三位之後沒有小於第三位的數,所以第三位為1。
用 1 / 1! = 1余0,說明 ,說明在第二位之後小於第四位的數有1個,所以第四位為

#include<bits/stdc++.h>
using namespace std;
int f[]={1,1,2,6,24,120};
int a[5];

void contorExpanse(){//康拓展開
    int ans=0;
    for(int i=0;i<5;i++){
        int tmp=0;
        for(int j=i+1;j<5;j++){
            if(a[j]<a[i]) tmp++;//計數
        }
        ans+=tmp*f[5-i-1];//當前計數*階乘
    }
    cout
<<ans<<endl; } void anticontorExpanse(int n){ //寫的非常混亂 /* 整理一下思路就是設計兩個動態數組作為可選和結果 從n->1開始,不斷的輾轉相除 計算當前小於該數的個數,從可選中選擇,並刪除 此時該數變成余數,重復步驟 */ vector<int>k; for(int i=1;i<=5;i++) k.push_back(i); vector<int>contor; int tmp=n;
for(int i=5;i>=1;i--){ int cc=tmp/(f[i-1]);//計算當前的計數 contor.push_back(k[cc]); k.erase(k.begin()+cc);//刪除 tmp=tmp%f[i-1];//變成余數 } for(int i=0;i<contor.size();i++) cout<<contor[i]<<" "; } int main(){ for(int i=0;i<5;i++) cin>>a[i]; contorExpanse(); int n; cin>>n; anticontorExpanse(n); return 0; }

【哈希!簡】康托展開與逆康托展開