康托展开是一个全排列到一个自然数的映射,可以快速求出一个全排列在所有全排列中字典序排第几
康托展开公式
$Large X=a_n*(n-1)!+a_{n-1}*(n-2)!+……+a_1*0!$
其中$a_i$表示全排列中后i个元素,第n-i+1个元素排第几(然后要减一)
证明
$a_i*(i-1)!$表示所有全排列中前n-i个元素与当前全排列相同,且第n-i+1个元素比当前全排列小的所有全排列都比当前全排列小
这样所有数相加就代表比当前全排列小的全排列的个数
代码实现
const int fac[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320};//阶乘,不够用可以再加 int cantor(int a[],int k){//康托展开 int ans=0,tmp; for(int i=0;i<k;i++){ tmp=0;//记录有几个比它小的数 for(int j=i+1;j<k;j++){ if(a[j]<a[i])tmp++; } ans+=tmp*fac[k-i-1]; } return ans; } void uncantor(int a[],int k,int num){//逆康托展开 int b[10];//b表示剩下的数,并且按升序排列 for(int i=0;i<k;i++)b[i]=i+1; b[k]=0; for(int i=0,x;i<k;i++){ x=num/fac[k-i-1],num%=fac[k-i-1],a[i]=b[x];//x表示当前数是剩下的数中的第几个 for(int j=x;b[j];j++){ b[j]=b[j+1]; } } }