康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
以下称第x个全排列是都是指由小到大的顺序。
可以将一个1,2,…,n的排列,映射到0,1,2,…,n!−1中的一个整数。
已知排列p1,p2,…,pn,映射到整数X。
X=a1(n−1)!+a2(n−2)!+⋯+an0!
ai表示小于pi的数字,在p1,p2,…,pi−1中没出现的个数。
显然0≤ai≤n−i。
例子:
{pi}=3,5,7,4,1,2,9,6,8
对应的
{ai}=2,3,4,2,0,0,2,0,0
最终
X=2×8!+3×7!+4×6!+2×5!+0×4!+0×3!+2×2!+0×1!+0×0!=98884
即有98884个排列小于{ai}。
第一个排列1,2,…,n对应的康托展开是0。
最后一个排列n,n−2,…,1对应的康托展开是n!−1。
int encode() {
int v[10] = {};
int re = 0;
for (int i = 0; i < 9; i++) {
int cnt = 0;
for (int j = 0; j < a[i]; j++) {
if (v[j] == 0) {
cnt++;
}
}
v[a[i]] = 1;
re += cnt * fac[8 - i];
}
return re;
}
首先根据最终的X计算出数组{ai}。
ai=⌊(n−i)!Xmod((n−i+1)!)⌋
然后根据数组{ai}计算出排列{pi}。
void decode(int x) {
int v[10] = {};
for (int i = 0; i < 9; i++) {
int cnt = x % fac[9 - i] / fac[8 - i];
for (int j = 0; j < 9; j++) {
if (v[j] == 0) {
if (cnt == 0) {
a[i] = j;
v[j] = 1;
break;
} else {
cnt--;
}
}
}
}
}
Luogu P2524
利用康托展开将排列映射到整数。
Luogu P1379
利用康托展开存储局面,进行BFS。