首页 > 代码库 > 康托展开与八数码问题

康托展开与八数码问题

康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! 其中,ai为当前未出现的元素中是排在第几个(从0开始)。


举个例子来说明一下:

例如,有一个数组 s = ["A", "B", "C", "D"],它的一个排列 s1 = ["D", "B", "A", "C"],现在要把 s1 映射成 X。

n 指的是数组的长度,也就是4,所以

X(s1) = a4*3! + a3*2! + a2*1! + a1*0!

关键问题是 a4、a3、a2 和 a1 等于什么?


a4 = "D" 这个元素在子数组 ["D", "B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,"D"是第3大的元素,所以 a4 = 3。
a3 = "B" 这个元素在子数组 ["B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,所以 a3 = 1。
a2 = "A" 这个元素在子数组 ["A", "C"] 中是第几大的元素。"A"是第0大的元素,"C"是第1大的元素,所以 a2 = 0。
a1 = "C" 这个元素在子数组 ["C"] 中是第几大的元素。"C" 是第0大的元素,所以 a1 = 0。(因为子数组只有1个元素,所以a1总是为0)

所以,X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20

 

八数码解法一:广搜+哈希

  考虑到费时主要在STL,对于大规模的遍历,用到了ST的set和string,在效率上的损失是很大的,因此,现在面临一个严重的问题,必须自己判重,为了效率,自然是自己做hash。有点麻烦,hash函数不好想,实际上是9!种排列,需要每种排列对应一个数字。网上搜索,得知了排列和数字的对应关系。取n!为基数,状态第n位的逆序值为哈希值第n位数。对于空格,取其为9,再乘以8!。例 如,1 3 7 24 6 9 5 8 的哈希值等于:0*0! + 2*1! + 0*2! + 1*3! + 3*4! +1*5! + 0*6! + 1*7! + 0*8! <9!具体的原因可以去查查一些数学书,其中1 2 34 5 6 7 8 9 的哈希值是0 最小,9 8 7 6 54 3 2 1 的哈希值是(9!-1)最大。而其他值都在0 到(9!-1) 中,且均唯一。然后去掉一切STL之后,甚至包括String之后,得到单向广搜+Hash的代码,算法已经可以在三秒钟解决问题,可是还是不够快!POJ时限是1秒,后来做了简单的更改,将路径记录方法由字符串改为单个字符,并记录父节点,得到解,这次提交,266ms是解决单问题的上限。当然,还有一个修改的小技巧,就是逆序对数不会改变,通过这个,可以直接判断某输入是否有可行解。由于对于单组最坏情况的输入,此种优化不会起作用,所以不会减少单组输入的时间上限。

 

康托展开与八数码问题