【哈希!简】康托展开与逆康托展开

康托展开是利用全排列与当前排列次序的映射建立一个简易哈希表

康托展开

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;
}

猜你喜欢

转载自www.cnblogs.com/rign/p/9974703.html