zoukankan      html  css  js  c++  java
  • 康托展开

    有一个关于排列问题的神奇的公式,康托展开

    维基百科是这样描述的:

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

    以下称第x个全排列是都是指由小到大的顺序。

    这可以用来求一个排列是全排列中的第几个排列...

    关于公式的话,我觉得这个大佬写得非常棒,可以往这边看 ACdreamer

    按照我个人的理解是,an表示的是第n个数(从最后一个数字开始从1数)的前面有几个比它小的数字,(n-1)!表示的是(当前的数的位数-1,从1开始数位数)的阶乘。

    贴个例题

    标题: 排列序数

    X星系的某次考古活动发现了史前智能痕迹。
    这是一些用来计数的符号,经过分析它的计数规律如下:
    (为了表示方便,我们把这些奇怪的符号用a~q代替)

    abcdefghijklmnopq 表示0
    abcdefghijklmnoqp 表示1
    abcdefghijklmnpoq 表示2
    abcdefghijklmnpqo 表示3
    abcdefghijklmnqop 表示4
    abcdefghijklmnqpo 表示5
    abcdefghijklmonpq 表示6
    abcdefghijklmonqp 表示7
    .....

    在一处石头上刻的符号是:
    bckfqlajhemgiodnp

    请你计算出它表示的数字是多少?

     这就是计算第几个全排列,如果不知道这个康托展开的话就很难做了...还是要多做题目 才能拓宽知识面...

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 long long a[17];
     4 
     5 int main(){
     6     a[0]=1;
     7     a[1]=1;
     8     for(int i=2; i<17; i++){
     9         a[i]=a[i-1]*i;
    10     }
    11     long long sum=0;
    12     string s="bckfqlajhemgiodnp";
    13     for(int i=0; i<17; i++){
    14         int tmp=0;
    15         for(int j=i+1; j<17; j++)
    16             if(s[j]<s[i]) tmp++;
    17         sum+=a[16-i]*tmp;
    18     }
    19     cout<<sum<<endl;
    20     
    21     return 0;
    22 }

    这个还有一个逆运用,就是已知一个数的第几个排列,让你去求这个数是什么...

    维基百科是这样描述的:

    既然康托展开是一个双射,那么一定可以通过康托展开值求出原排列,即可以求出n的全排列中第x大排列。

    如n=5,x=96时:

    首先用96-1得到95,说明x之前有95个排列.(将此数本身减去!)
    用95去除4! 得到3余23,说明有3个数比第1位小,所以第一位是4.
    用23去除3! 得到3余5,说明有3个数比第2位小,所以是4,但是4已出现过,因此是5.
    用5去除2!得到2余1,类似地,这一位是3.
    用1去除1!得到1余0,这一位是2.
    最后一位只能是1.
    所以这个数是45321.
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 int main(){
     5     long long p[16];
     6     p[0]=p[1]=1;
     7     for(int i=2; i<16; i++)
     8         p[i]=p[i-1]*i;
     9 
    10     int n, m, tmp;
    11     cin>>n>>m;
    12     tmp=n;
    13     vector<int>a;
    14     vector<int>v;
    15     for(int i=1; i<=n; i++){
    16         a.push_back(i);
    17     }
    18     m--;
    19     int t, r;
    20     for(int i=n; i>=1; i--){
    21         t=m/p[i-1];
    22         m=m%p[i-1];
    23         v.push_back(a[t]);
    24         a.erase(a.begin()+t);
    25         sort(a.begin(), a.end());
    26     }
    27     vector<int>::iterator it;
    28     for(it=v.begin(); it!=v.end(); it++){
    29         cout<<*it;
    30     }
    31     cout<<endl;
    32     
    33     return 0;
    34 }
  • 相关阅读:
    第05篇:C#星夜拾遗之使用数据库 拓荒者
    第02篇:C#星夜拾遗之Windows窗体 拓荒者
    移动“我的文档” 2010年5月22日学习笔记(1) 拓荒者
    为Windows Live Writer写一个简单的插件 拓荒者
    [转] 关于VisualC++的ATL、MFC、CLR对比 拓荒者
    如何在Windows 2003 中使用Windows Live Writer? 2010年5月21日学习笔记(1) 拓荒者
    第01篇:C#星夜拾遗之如何开始C#学习 拓荒者
    JavaScript Mobile开发框架汇总
    CSS调用远程字体
    locale的详细解释
  • 原文地址:https://www.cnblogs.com/ledoc/p/6572714.html
Copyright © 2011-2022 走看看