康托展开

        最近在搞新加坡的NOI2012的题,其中第二题Pancake可以用BFS解决,不过现在正在研究盖茨的《Bounds For Sorting By Prefix Reverse》,等研究出来一些成果会发布。目前能搞定的是BFS算法, 好在n最大为8,枚举量最多为8!,虽说有每个测试点可能有6000个测试数据,但还算可以接受。在BFS中,可以利用康托展开进行优化,本文主要对康托展开进行一下介绍。

        康托展开是一个全排列到自然数的双射,康托展开其实计算的是当前排列在全排列中由小到大的顺序(最小的顺序编号为0)。而且可以通过逆运算还原原值。

        康托展开公式:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!(其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n,a[i]指比第i个数字(姑且设为x)小(从右向左数)小且没在从左起到x之间出现过的数字的个数,换句话说,a[i]也就是从右数第i个数字的右面比它小的数字的个数)

        这样说这个公式可能让人难以理解,我们还是举一个例子吧。

        比如下面这个排列:3 5 7 4 1 2 9 6 8,第一个数是3,在3的右面有两个比它小的数,所以a[i]=2,第一项为2*8!,第二项是5,在5的右面有3个比它小的数,所以第二项是3*7!...以此类推,所以这个排列的康托展开2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884

        由于阶乘的出现,很明显在实际应用的过程中,n在10以内适用康托展开,因为如果n比较大的话,阶乘值也会很大,那么使用康托展开进行排列优化的意义就不大了,而且计算机存储也颇为不便。

        下面将给出把一个排列(排列的元素为从1~n)转化为康托展开和把康托展开转化成排列的C++程序:

 
  1. //cantor.cpp by JerryXie   
  2.   
  3. #include<cstdio>    
  4. using namespace std;   
  5.   
  6. int n,bin[11],a[50001],b[50001],num;   
  7. bool t[50001];   
  8.     
  9. void create() //初始化0~10的阶乘值    
  10. {   
  11.   int i;   
  12.   bin[0]=1;   
  13.   bin[1]=1;   
  14.   for(i=2;i<=10;i++)   
  15.     bin[i]=bin[i-1]*i;   
  16. }   
  17.   
  18. void atonum() //排列转换为值    
  19. {   
  20.   int i,j;   
  21.   for(i=1;i<=n;i++)   
  22.     for(j=i+1;j<=n;j++)   
  23.       if(a[i]>a[j])   
  24.         b[i]++;   
  25.   num=0;   
  26.   for(i=1;i<=n;i++)   
  27.     num+=b[i]*bin[n-i];   
  28. }   
  29.   
  30. void numtoa() //值转换为排列    
  31. {   
  32.   int i,yushu,div,temp;   
  33.   void translate();   
  34.   temp=num;   
  35.   for(i=1;i<=n;i++)   
  36.   {   
  37.     div=temp/bin[n-i];   
  38.     yushu=temp%bin[n-i];   
  39.     b[i]=div;   
  40.     temp=yushu;   
  41.   }   
  42.   b[n]=0;   
  43.   translate();   
  44. }   
  45.   
  46. void translate() //计算出的结果转化为原始值    
  47. {   
  48.      int i,j,s;   
  49.      for(i=0;i<=n+1;i++)   
  50.        t[i]=true;   
  51.      a[1]=b[1]+1;   
  52.      t[a[1]]=false;   
  53.      for(i=2;i<=n;i++)   
  54.      {   
  55.        s=0;   
  56.        for(j=1;j<=n;j++)   
  57.          if(t[j]==true)   
  58.          {   
  59.            s++;   
  60.            if(s==b[i]+1)   
  61.            {   
  62.              a[i]=j;   
  63.              t[j]=false;   
  64.              break;   
  65.            }   
  66.          }   
  67.      }   
  68. }   
  69.   
  70. int main() //主函数    
  71. {   
  72.     freopen("cantor.in","r",stdin);   
  73.     freopen("cantor.out","w",stdout);   
  74.     int i,sign;   
  75.     create();   
  76.     scanf("%d",&sign);   
  77.     if(sign==1) //排列转换为值    
  78.     {   
  79.       scanf("%d",&n);   
  80.       for(i=1;i<=n;i++)   
  81.         scanf("%d",&a[i]);   
  82.       atonum();   
  83.       printf("%d",num);   
  84.     }   
  85.     else //值转换为排列    
  86.     {   
  87.       scanf("%d",&n);   
  88.       scanf("%d",&num);   
  89.       numtoa();   
  90.       for(i=1;i<=n;i++)   
  91.         printf("%d ",a[i]);   
  92.     }   
  93.     return 0;   
  94. }   

 

欢迎鄙视。

posted @ 2015-09-03 20:58  JerryXie  阅读(152)  评论(0编辑  收藏  举报