zoukankan      html  css  js  c++  java
  • 康拓展开和康拓逆展开

      X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!。这就是康托展开。    ------来自百度百科

    上面这句话说了感觉一头雾水,没有任何条件只是给一个式子谁也看不懂那是什么,所以还需要来解释一下。上面的式子可以在求给定一个全排列,求在整个全排列序列中的第几个。

    例如:{1,2,3}按从小到大排列一共6个。123 132 213 231 312 321 。如果想知道 3 2 1 在这个排列序列中数第几位这时候需要用到康拓展开了。

      第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个大的数。 2*2!+1*1!是康托展开。

      再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

    康拓展开代码:

     1 //s[] 表示输入进来的那个序列, n表示总共多少个元素
     2 int cantor(int s[], int n)
     3 {
     4     
     5     int ans = 0;
     6     int tmp;
     7     
     8         for (int i = 0; i < n - 1; i++)
     9         {
    10             tmp = 0;
    11             //统计多少个比它小的
    12             for (int j = i + 1; j < n; j++)
    13                 if (s[j] < s[i])
    14                     tmp++;
    15             ans += tmp * fac[n - i - 1];
    16         }
    17     return ans;
    18     
    19 }    
    View Code

    另外为了熟悉一下这个康拓展开,还是在OJ上测试一下,题目链接http://acm.nyist.net/JudgeOnline/problem.php?pid=139

    另外这个题目的AC代码

     1 #include<iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 
     5 typedef long long LL;
     6 LL fac[13];
     7 const int n = 12;
     8 LL cantor(int *arr)
     9 {
    10     LL ans = 0;
    11     for (int i = 0; i < n - 1; i++)
    12     {
    13         int tmp = 0;
    14         for (int j = i + 1; j < n; j++)
    15             if (arr[j] < arr[i])
    16                 tmp++;
    17         ans += tmp * fac[n - i - 1];
    18     }
    19     return ans;
    20 }
    21 int main()
    22 {
    23 
    24     fac[0] = fac[1] = 1;
    25     for (int i = 2; i <= 12; i++)
    26         fac[i] = fac[i - 1] * i;
    27     char ch[13];
    28     int arr[13];
    29     int n;
    30     cin >> n;
    31     while (n--)
    32     {
    33         scanf("%s", ch);
    34         for (int i = 0; i < 12; i++)
    35             arr[i] = ch[i] - 'a' + 1;
    36         cout << cantor(arr) + 1 << endl;
    37     } 
    38     return 0;
    39 }
    View Code

      康拓展开逆运算:还是同样的序列,给定一个数m,让求第m大的序列

      {1,2,3,4,5}的全排列,并且已经从小到大排序完毕,请找出第96个数:

       首先用96-1得到95

       用95去除4! 得到3余23,即有3个数比该数位上的数字小,则该数位的数字为4;
       用23去除3! 得到3余5,即有3个数比该数位上的数字小,理应为4,但4已在前面的高位中出现过,所以该数位的数字为5;
       用5去除2!得到2余1,即有2个数比该数位上的数字小,则该数位的数字为3;
       用1去除1!得到1余0,即有1个数比该数位上的数字小,则该数位的数字为2;
       最后一个数只能是1;
       所以这个数是45321
    C代码
     1 //index表示给定的第几个数,结果序列保存在ans数组
     2 void cantor_reverse(int index, int n, int *ans)
     3 {
     4     index--;
     5     bool vis[10];//标记
     6     memset(vis, false, sizeof(vis));
     7     for (int i = 0; i < n; i++)
     8     {
     9         int tmp = index /fac[n - i - 1];
    10         for (int j = 0; j <= tmp; j++)
    11             if (vis[j])
    12                 tmp++;
    13         ans[i] = tmp + 1;
    14         vis[tmp] = true;
    15         index %= fac[n - i - 1];
    16     }
    17 }
    View Code

    OJ题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=143

    AC代码:

     1 #include<iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 using namespace std;
     5 
     6 typedef long long LL;
     7 LL fac[13];
     8 void cantor_reverse(LL index, int *ans)
     9 {
    10     index--;
    11     LL tmp;
    12     bool vis[13];
    13     memset(vis, false, sizeof(vis));
    14     for (int i = 0; i < 12; i++)
    15     {
    16         tmp = index / fac[12 - i - 1];
    17         for (int j = 0; j <= tmp; j++)
    18             if (vis[j])
    19                 tmp++;
    20         ans[i] = (int)tmp + 1;
    21         vis[tmp] = true;
    22         index %= fac[12 - i - 1];
    23     }
    24 }
    25 int main()
    26 {
    27     fac[0] = fac[1] = 1;
    28     for (int i = 2; i < 13; i++)
    29         fac[i] = fac[i- 1] * i;
    30     int n;
    31     LL m;
    32     cin >> n;
    33     while (n--)
    34     {
    35         cin >> m;
    36         int ans[13];
    37         cantor_reverse(m, ans);
    38         for (int i = 0; i < 12; i++)
    39             printf("%c", ans[i] + 'a' - 1);
    40         puts("");
    41     }
    42 
    43     return 0;
    44 }
    View Code
  • 相关阅读:
    zoj 3279 线段树 OR 树状数组
    fzu 1962 树状数组 OR 线段树
    hdu 5057 块状链表
    hdu3487 Play with Chain
    bzoj 1588营业额统计(HNOI 2002)
    poj2823 Sliding Window
    poj2828 Buy Tickets
    poj2395 Out of Hay
    poj3667 Hotel
    poj1703 Lost Cows
  • 原文地址:https://www.cnblogs.com/Howe-Young/p/4348777.html
Copyright © 2011-2022 走看看