概念:
康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。(百度百科)
康拓展开:
给定一个全排列序列,求该序列是所有全排列序列中字典序第几的序列
公式如下:
其中, a[i] 为整数,并且 0<=a[i]<i,1<=i<=n。
- a[i]表示位于位置i后面的数小于a[i]值的个数。
- X为小于该排列的个数,所以该排列的次序应该是X+1。
康拓展开代码:
cin>>n;
fac[0]=1;
for(int i=1;i<=n;i++)//预处理阶乘
fac[i]=fac[i-1]*i;
int ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int k=0;
for(int j=i+1;j<=n;j++)
if(a[j]<a[i])k++; ///统计位于a[i]后且比a[i]小的数的个数
ans+=k*fac[n-i];
}
cout<<ans+1<<endl; ///注意,记得+1
逆康拓展开:
给定全排列大小n,字典序k,求字典序为k的排列
逆康拓展开代码:
vector<int>y;
void ni_Cantor(int len,int num)
{
num=num-1; ///注意
vector<int>a;
for(int i=1;i<=len;i++)
a.push_back(i);
for(int i=1;i<=len;i++){
int t=num/ans[len-i];
num=num%ans[len-i];
y.push_back(a[t]); ///剩余数里第t+1个数为当前位
a.erase(a.begin()+t); ///删除选做当前位的数
}
}
应用:
- 康托展开是一个数组到一个数的映射,可以应用于hash中进行空间压缩。例如,在八数码问题中,我们可以把一种排列状态压缩成一个整数存放在数组中。(获取排列的id,构建hash表)
- 计算关于排列序列的问题
康拓展开及逆康拓展开:
#include<iostream>
#define _ ios::sync_with_stdio(false);
using namespace std;
int ans[12]; ///存储阶乘
void factorial(int q) ///预处理出0!~q!
{
ans[0]=1;
for(int i=1;i<=q;i++)
ans[i]=ans[i-1]*i;
}
/*康拓展开*/
int Cantor(int n,int x[]) ///n表示n个数的排列,x[]为传递过来的排列数组
{
int cnt=0,sum;
for(int i=1;i<=n;i++){
sum=0;
for(int j=i+1;j<=n;j++){
if(x[j]<x[i])
sum++;
}
cnt+=sum*ans[n-i];
}
return cnt+1;
}
/*逆康拓展开*/
int* ni_Cantor(int len,int num,int y[]) ///len表示len个数的排列,num表示次序,y[]存储生成的排列数组
{
bool book[len+2]={0};
int sum2,k=0,t;
num=num-1;
for(int i=1;i<=len;i++){
sum2=0;
t=num/ans[len-i];
num=num%ans[len-i];
for(int j=1;j<=len;j++) ///对比上面的逆康拓展开做法,利用vector的特性,而不需要去for嵌套,降低了复杂度
if(!book[j]){
sum2++;
if(sum2==t+1){
y[k++]=j;
book[j]=1;
break;
}
}
}
return y;
}
int main(){_
factorial(10); ///预处理出0!~10!
int T,a;
cin>>T; ///T个测试用例
while(T--)
{
int x[15]={},y[15],n,a,m;
cin>>a;
///a为0时,输出该排列的次序编号;
///a为1时,输出n个正整数,表示这个次序对应的一个全排列(每个数字后面跟一个空格)。
if(a==0){
cin>>n; ///0<n<10
for(int i=1;i<=n;i++)
cin>>x[i];
cout<<Cantor(n,x)<<endl;
}
else if(a==1){
cin>>n>>m; ///n表示n个数的全排列,m表示次序编号为m的全排列
ni_Cantor(n,m,y);
for(int i=0;i<n;i++)
cout<<y[i]<<' ';
cout<<endl;
}
}
return 0;
}