标签:
最近在搞新加坡的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++程序:
-
- #include<cstdio>
- using namespace std;
-
- int n,bin[11],a[50001],b[50001],num;
- bool t[50001];
-
- void create()
- {
- int i;
- bin[0]=1;
- bin[1]=1;
- for(i=2;i<=10;i++)
- bin[i]=bin[i-1]*i;
- }
-
- void atonum()
- {
- int i,j;
- for(i=1;i<=n;i++)
- for(j=i+1;j<=n;j++)
- if(a[i]>a[j])
- b[i]++;
- num=0;
- for(i=1;i<=n;i++)
- num+=b[i]*bin[n-i];
- }
-
- void numtoa()
- {
- int i,yushu,div,temp;
- void translate();
- temp=num;
- for(i=1;i<=n;i++)
- {
- div=temp/bin[n-i];
- yushu=temp%bin[n-i];
- b[i]=div;
- temp=yushu;
- }
- b[n]=0;
- translate();
- }
-
- void translate()
- {
- int i,j,s;
- for(i=0;i<=n+1;i++)
- t[i]=true;
- a[1]=b[1]+1;
- t[a[1]]=false;
- for(i=2;i<=n;i++)
- {
- s=0;
- for(j=1;j<=n;j++)
- if(t[j]==true)
- {
- s++;
- if(s==b[i]+1)
- {
- a[i]=j;
- t[j]=false;
- break;
- }
- }
- }
- }
-
- int main()
- {
- freopen("cantor.in","r",stdin);
- freopen("cantor.out","w",stdout);
- int i,sign;
- create();
- scanf("%d",&sign);
- if(sign==1)
- {
- scanf("%d",&n);
- for(i=1;i<=n;i++)
- scanf("%d",&a[i]);
- atonum();
- printf("%d",num);
- }
- else
- {
- scanf("%d",&n);
- scanf("%d",&num);
- numtoa();
- for(i=1;i<=n;i++)
- printf("%d ",a[i]);
- }
- return 0;
- }
欢迎鄙视。
康托展开
标签:
原文地址:http://www.cnblogs.com/jerryxie/p/4780695.html