康拓展开概念引入
今天在解八数码问题时,用DFS直接穷举暴力搜索,一直爆栈。无奈,只能参考别人的代码,同样是DFS却PASS了。看了下他的代码,DFS深度写死了,如下:
void DFS(int x, int y)
{
if(step > 8)
return;
...
}
很好奇为啥只需要8步?难道无论怎么摆放,要达到目标状态,一定不会超过8步就可以完成么?我把自己的代码也加了这个条件,直接PASS。。。震惊!!
带着这个问题继续上网搜索相关资料,了解到这个问题可能无解,也有需要30多步才能解决的。那这样看来,就是系统的判定case太弱了,所以深度只需要8就可以AC。。
搜罗了很久,发现网上大部分答案都是BFS,A*…,其中他们用到的康拓展开来保存状态,回退状态。当然我也是把这九个数字组成的状态映射成一个数字来保存,但是感觉就是没康拓展开来的科学。所以决定看下康拓展开是怎么一回事。
定义
康拓展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。康拓展开的实质是计算当前排列在所有由小到大全排列中的顺序,并且是可逆的。
比如123的全排列总共6个,通过康拓展开可以计算出由小到大,231排在第几个。
公式
康拓展开就是指把一个字符、数字序列展开成如下形式:
举个栗子:
2 5 4 1 3展开为46。因为X=1*4!+33!+2*2!+0*1!+0*0!=46.
解释:
- 排列的第一位是2,比2小的数有一个,以这样的数开始的排列有4!个,因此第一项为 1*4!。
- 排列的第二位是5,比5小的数有四个,由于2已经出现,因此共有3个比5小的数,这样的排列有33!个,以此类推,直至00!
可见在八数码问题中,9位数字的排列组合可以通过康拓展开来把他们映射成一个数,方便保存和利用,压缩了空间。
康拓展开代码如下:
const int fac[]={1,1,2,6,24,120,720,5040,40320,362880}; //各阶乘结果0!,1!,2!,3!,4!...
//康拓展开函数
int cantor(int a[], int len)
{
int ans = 0;
for(rint i = 0; i < len; i++) //a数组是给出的数,n是元素个数
{
int cnt = 0; //统计有多少数比a[i]小
for(rint j = i+1; j < len; j++)
{
if(a[i] > a[j])
cnt++;
}
ans+=cnt*fac[len-i];
}
return ans;
}
int main()
{
int a[4] = {1, 2, 4, 3};
printf("%d", cantor(a, 4));
return 0;
}
逆康拓展开
假如给你一个在全排列中排在第几位的数,求出这个排列组合的具体表示,这便是你康拓展开。还是用上面的代码例子。
假如原来的数组1234,求他排在第24位的组合,如何求? 排序是从0开始,那么这里应当首先 24-1 = 23
* 23 / 3! = 3 余 5, 那么表示比他小的数有3个,即 4。
* 5 / 2! = 2 余 1, 那么表示比他小的数有2个,即 3。
* 1 / 1! = 1 余 0, 那么表示比他小的数有1个,即 2。
* 0 / 0! = 0 余 0, 那么表示比他小的数有0个,即 1。
* 算出这个组合便是:4321
逆康拓展开代码如下:
#include<stdio.h>
#define rint register int
const int fac[]={1,1,2,6,24,120,720,5040,40320,362880}; //各阶乘结果0!,1!,2!,3!,4!...
void cantorReverse(int a[], int len, int k)
{
int temp;
int vis[20] = {0};//记录数字的使用情况
for(rint i = 0; i < len; i++)
{
temp = k/fac[len - 1 - i];
k %= fac[len - 1 - i];
for(rint j = 0; j <= temp; j++)
{
if(vis[j])//如果有比他小的数已经用过了
temp++;
}
a[i] = temp + 1;
vis[temp] = 1;
}
}
int main()
{
int a[4] = {0};
int k = 16;
cantorReverse(a, 4, k);
printf("%d %d %d %d",a[0],a[1],a[2],a[3]);
return 0;
}
总结
康拓展开和逆展开可以用于把字符,数字的组合排列映射成一个数字,用于哈希表的构造,将字符序列或者数字序列映射成一个值或者状态。
本文原创首发于微信公众号 [ 林里少年 ],欢迎关注第一时间获取更新。