菜鸟就要老老实实重新学起:
康托展开/逆康托展开:
主要就是用来求全排列的次序,可用于取某序列的hash值。
eg:4231,4位数总共有4!种排列方式,因为小于4的数有3个,所以后面就有3*3!=18种排列方式,对于2后面有一个小于它的数1,就有1*2!=2种排列,对于3同样有1*1!=1种排列,对于1后面没有数字了,所以这个排列就是第21个排列。
///康托展开
{
int fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]
/* 康托展开.{1...n}的全排列由小到大有序,s[]为第几个数 */
int KT(int n, int s[])
{
int i, j, t, sum;
sum = 0;
for (i=0; i<n; i++)
{
t = 0;
for (j=i+1; j<n; j++)
if (s[j] < s[i])
t++;
sum += t*fac[n-i-1];
}
return sum+1;
}
}
///逆康托展开
{
int fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]
/* 康托展开的逆运算. {1...n}的全排列,中的第k个数为s[] */
void invKT(int n, int k, int s[])
{
int i, j, t, vst[8]={0};
k--;
for (i=0; i<n; i++)
{
t = k/fac[n-i-1];
k %= fac[n-i-1];
for (j=1; j<=n; j++)
if (!vst[j])
{
if(t==0)break;
t--;
}
s[i] = j;
vst[j] = 1;
}
}
}