原理:
举个例子来说明康拓展开的应用:
已知1,2,3,4,5五个数的全排列,给出一个排列34152,问该排列在全排列中是第几个。而康托展开的值就是这个排名。
- 首位是3,比它小而且没有出现过的数有1,2两个,所以为 2 * 4!;
- 第二位是4,比它小而且没有出现过的数有1,2两个,所以值为 2 * 3!;
- 第三位是1,没有比它小而且没有出现过的数,所以值为 0 * 2!;
- 第四位是5,比它小而且没有出现过的数有2一个,所以值为 1 * 1 !;
- 第五位是2,没有比它小而且没有出现过的数,所以值为 0 * 0!;
最终相加的结果是61,所以34152在全排列中是第61个。
实现的代码为:
1 int FAC[20]; // 阶乘 2 int cantor(int *a, int n) 3 { 4 FAC[1] = 1; 5 for(int i = 1; i <= 20; i++)//求阶乘 6 FAC[i] = FAC[i-1] * i; 7 int x = 0; 8 for (int i = 0; i < n; ++i) { 9 int smaller = 0; // 在当前位之后小于其的个数,这里很神奇, 10 for (int j = i + 1; j < n; ++j) {//最终的结果就是小于该数却没有出现过的数的个数 11 if (a[j] < a[i]) 12 smaller++; 13 } 14 x += FAC[n - i - 1] * smaller; // 康托展开累加 15 } 16 return x; // 康托展开值 17 } 18 /*这里在求小于该数却没有出现过的数的时候,如果n很大就会有超时的危险,可以用线段树来实现*/
或
1 int FAC[20]; // 阶乘 2 int vis[maxn]; 3 int cantor(int *a, int n) 4 { 5 FAC[1] = 1; 6 for(int i = 1; i <= 20; i++)//求阶乘 7 FAC[i] = FAC[i-1] * i; 8 int x = 0; 9 for (int i = 0; i < n; ++i) { 10 int smaller = 0; 11 vis[a[i]] = 1; 12 for (int j = 0; j < i; ++j) { 13 if (!vis[j] && a[j] < a[i]) 14 smaller++; 15 } 16 x += FAC[n - i - 1] * smaller; // 康托展开累加 17 } 18 return x; // 康托展开值 19 } 20 /*从前边开始的统计如上*/