康托展开

康托展开


S = a[n] * (n)! + a[n - 1] * (n - 1)! + a[n - 2] * (n - 2)! + ......... + a[1] * 1! + a[0] * 0!;

设由一个排列P = 4321, 则p[3] = 4, p[2] = 3, p[1] = 2, p[1] = 1,我们的a[n]表示sum(1 | a[j] < a[i], j > i),就是当前这一项后比这个项小的数的个数。

这里最后得到的S值就是当前排列在全排列中的序号,也就是第几个排列为当前排列,主要用于将全排列hash存储,可以极大压缩空间,n^n -> n!,这里S的区间为[0, n! - 1]

由于康托展开后是一对一的关系,我们可以将S值还原回原序列。演示如下


P = 4 1 3 2


S = 3 * 3! + 0 * 2! + 1 * 1! + 0 * 0! = 19;


19 / 3! = 3 ........1//表示有3个数比当前数小 这里就是4

1   / 2! = 0.........1//表示有0个数比当前数小 这里就是1

1   / 1! = 1.........0//表示有1个数比当前数小 有1个比当前数小,这里应是2,但是1在之前的位置出现过了,不能在后面出现了,所以这里是3

0   / 0! = 0.........0//表示没有数比当前数小,这里只有2了


每一步除以对应的阶乘,将余数作为下一步的被除数,将商保留,还原为原序列

所以原序列就是4 1 3 2


康托展开&逆康托展开


int fact(int x){
    if(x == 0)  return 1;
    return x * fact(x - 1);
}

int KangTuo(char s[], int n){//s为排列,n为排列元素个数
    int i, j, k;
    int ret = 1;
    for(i = 0; i < n; i++){
        int tot = 0;
        for(j = i + 1; j < n; j++){
            if(s[j] < s[i]){
                tot++;
            }
        }
        ret += tot * fact(n - 1 - i);
    }
    return ret;
}

void reKangTuo(int s, int n, char ans[]){//s康托展开的结果,n为排列元素个数, ans为还原序列
    int i, j, k;
    bool flag[n];
    memset(flag, false, sizeof(flag));
    s--;
    for(i = 0; i < n; i++){
        int tmp = s / fact(n - 1 - i);
        s = s % fact(n - 1 - i);
        int tot = 0;
        for(j = 0; j < n; j++){
            if(tot == tmp && !flag[j])  break;
            if(!flag[j]){
                tot++;
            }
        }
        ans[i] = j + 1;
        flag[j] = true;
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值