康拓展开和康拓逆展开

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31736627/article/details/69367440

前言:
今天做一道排列组合的题目,然后发现那道题目要用到康拓展开,于是自学了一下康拓展开和逆展开。
是什么
她是什么呢?
定义:X=an*(n-1)!+an-1*(n2)!+…+ai(i1)!+…+a2*1!+a1*0!ai为整数,
并且0<=ai < i (1<=i<=n)简单点说就是,判断这个数在其各个数字全排列中从小到大排第几位。
比如 132,在1、2、3的全排列中排第2位
这个是代码:
作用:
她的作用是什么呢?
维基:n位(0~n-1)全排列后,其康托展开唯一且最大约为n!,因此可以由更小的空间来储存这些排列。由公式可将X逆推出对应的全排列。它可以应用于哈希表中空间压缩,而且在搜索某些类型题时,将VIS数组量压缩。比如:八数码、魔板。。(不过现在还没遇到过)

康托展开求法:
比如2143 这个数,求其展开:从头判断,至尾结束
① 比 2(第一位数)小的数有多少个->1个就是1,1*3!
② 比 1(第二位数)小的数有多少个->0个0*2!
③ 比 4(第三位数)小的数有多少个->3个就是1,2,3,但是1,2之前已经出现,所以是 1*1!将所有乘积相加=7
比该数小的数有7个,所以该数排第8的位置。

1234 1243 1324 1342 1423 1432
2134 2143 2314 2341 2413 2431
3124 3142 3214 3241 3412 3421
4123 4132 4213 4231 4312 4321

(发现我的代码和大牛的简直没得比 就不贴出来了)

int  fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]  
// 康托展开-> 表示数字a是 a的全排列中从小到大排,排第几  
// n表示1~n个数  a数组表示数字。  
int kangtuo(int n,char a[])  
{  
    int i,j,t,sum;  
    sum=0;  
    for( i=0; i<n ;++i)  
    {  
        t=0;  
        for(j=i+1;j<n;++j)  
            if( a[i]>a[j] )  
                ++t;  
        sum+=t*fac[n-i-1];  
    }  
    return sum+1;  
}  

康托逆展开:
因为她可以作为hash 所以有一定的映射关系,所以肯定存在逆展开式,她的作用就是求n位数第m个排列的组合形式。

假设求4位数中第19个位置的数字。

① 19减去1 → 18

② 18 对3!作除法 → 得3余0

③ 0对2!作除法 → 得0余0

④ 0对1!作除法 → 得0余0

据上面的可知:

我们第一位数(最左面的数),比第一位数小的数有3个,显然 第一位数为→ 4

比第二位数小的数字有0个,所以 第二位数为→1

比第三位数小的数字有0个,因为1已经用过,所以第三位数为→2

第四位数剩下 3

该数字为 4123 (正解)

下面是代码:

int  fac[] = {1,1,2,6,24,120,720,5040,40320}; 
//这里实际用的时候可以先打表计算出来fac[i]就是i!是几 
//康托展开的逆运算,{1...n}的全排列,中的第k个数为s[]  
void reverse_kangtuo(int n,int k,char s[])  
{  
    int i, j, t, vst[8]={0};  
    --k;  
    for (i=0; i<n; i++)  
    {  
        t = k/fac[n-i-1];  
        for (j=1; j<=n; j++)  
            if (!vst[j])  
            {  
                if (t == 0) break;  
                --t;  
            }  
        s[i] = '0'+j;  
        vst[j] = 1;  
        k %= fac[n-i-1];  
    }  
}  

猜你喜欢

转载自blog.csdn.net/qq_31736627/article/details/69367440
今日推荐