• 康托展开学习模板


    定义:

    康托展开是一个全排列到一个自然数双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

    康托展开的用法:

      有一个以元素{1,2,...,n}为排列元素的全排列:

      1.  给定一个全排列序列,求该序列是所有全排列序列中字典序第几的序列

      2. (逆康托展开)给定全排列大小n,字典序k,求字典序为k的排列

    公式及原理:

     V = A[0]*(n-1)! + A[1]*(n-2) +...+ A[n-1]*0!

     * A[i]对应的是位于位置i后的数的个数,乘(n-i-1)的阶乘

     V的值对应上述康托展开的用法中序列的康托展开值,也就是字典序排名

    听不懂在说什么?没事,让我们看看下面的例子

    例:

      在给定的序列{1,2,3,4,5,6}组成的全排列中,计算3 5 6 2 1 4对应的康托展开值

      * V = 2*5! + 3*4! + 3*3! + 1*2! + 0*1! + 0*0!

        = 240 + 72 + 18 + 2 + 0 +0 

        = 332

      ps:求得的康托展开值是从0开始的,也就意味着 V+1才是相应的排列在原序列中的字典序排名

    康托展开代码:

     1 int cantor(int *a,int n) {  ///a[]:原数组,下标从1开始
     2     int ans=0;
     3     for(int i=1;i<n;i++) {
     4         int m=1,rk=0;
     5         for(int j=i+1;j<=n;j++) {
     6             if(a[j]<a[i])
     7                 rk++;
     8             m*=(j-i);   ///阶乘
     9         }
    10         ans+=rk*m;      ///据公式X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0!
    11                         ///A[i] = rk
    12     }
    13     return ans+1;       ///ans+1,即为原序列在全排列中的次序
    14 }
    View Code

     

    逆康托展开:

    给定全排列大小n,字典序k,求字典序为k的排列

    例:

      在{1,2,3,4,5,6}中给出字典序排名为666可以算出排列组合为6 3 4 5 2 1

      具体过程:

      666 - 1 = 665;

      665 / 5! = 5 余 65 说明首位排名第 6 ,即首位为6;

        65 / 4! = 2 余 17 说明第二位排名第 3 ,即第二位为3;

        17 / 3! = 2 余   5 说明第三位排名第 3 ,即第三位为4;

          5 / 2! = 2 余   1 说明第四位排名第 3 ,即第四位为5;

        1 / 1! = 1  余  0 说明第五位排名第 1 ,即第五位为2;

       最后一位即为剩下的1。

    逆康托展开代码:

     1 void decantor(int x,int n,int *a) {
     2     bool vis[10];
     3     memset(vis,false,sizeof(vis));
     4     for(int i=0;i<n;i++) {
     5         int rk=x/f[n-i-1];
     6         for(int j=0;j<=rk;j++)      ///与上述同理,已访问过的数不在序列中,所以要将rk++
     7             if(vis[j]) rk++;
     8         a[i]=rk+1;          ///求得的rk是比A[i]小的数的个数,所以A[i]=rk+1
     9         vis[rk]=true;
    10         x%=f[n-i-1];
    11     }
    12 }
    View Code

    下面给出完整代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 int f[]={1,1,2,6,24,120,720,5040,40320,362880}; ///算出1-9的阶乘,0的阶乘用1代替
     6 int cantor(int *a,int n) {
     7     int ans=0,rk;
     8     for(int i=1;i<n;i++) {
     9         rk=0;
    10         for(int j=i+1;j<=n;j++)   ///计算位于i后面的数小于a[i]的个数,rk即为i的排名
    11             if(a[j]<a[i])
    12                 rk++;
    13         ans+=rk*f[n-i];     ///据公式X = A[0] * (n-1)! + A[1] * (n-2)! + … + A[n-1] * 0!
    14                             ///A[i] = rk
    15     }
    16     return ans+1;           ///ans+1,即为原序列在全排列中的次序
    17 }
    18 void decantor(int x,int n,int *a) {
    19     bool vis[10];
    20     memset(vis,false,sizeof(vis));
    21     for(int i=0;i<n;i++) {
    22         int rk=x/f[n-i-1];
    23         for(int j=0;j<=rk;j++)      ///与上述同理,已访问过的数不在序列中,所以要将rk++
    24             if(vis[j]) rk++;
    25         a[i]=rk+1;          ///求得的rk是比A[i]小的数的个数,所以A[i]=rk+1
    26         vis[rk]=true;
    27         x%=f[n-i-1];
    28     }
    29 }
    30 int main()
    31 {
    32     int t,n,oper,a[11],v;
    33     cin>>t;
    34     while(t--) {
    35         cin>>oper;
    36         if(!oper) {
    37             cin>>n;
    38             for(int i=1;i<=n;i++)
    39                 cin>>a[i];
    40             cout<<cantor(a,n)<<endl;
    41         }
    42         else {
    43             cin>>n>>v;
    44             decantor(v-1,n,a);
    45             for(int i=0;i<n;i++)
    46                 cout<<a[i]<<' ';
    47             cout<<endl;
    48         }
    49     }
    50     return 0;
    51 }
    View Code
  • 相关阅读:
    Git哲学与使用
    save
    http://www.onvif.org/onvif/ver20/util/operationIndex.html
    图标
    C#高性能大容量SOCKET并发(一):IOCP完成端口例子介绍(转)
    一种基于PTP 协议的局域网高精度时钟同步方法(转)
    WPF中的数据模板(DataTemplate)(转)
    WPF中的ControlTemplate(控件模板)(转)
    也来说说C#异步委托(转)
    C#委托的介绍(delegate、Action、Func、predicate)(转)
  • 原文地址:https://www.cnblogs.com/wuliking/p/10804741.html
Copyright © 2020-2023  润新知