[UVA10181]十五数码解题报告

对于有解的情况,只需ID-A*即可。
首先谈一谈估价函数,估价函数表示的应该是对期望步数的下界。我一开始想的是用所有数现在的位置到应该在的位置的曼哈顿距离和,考虑到一次交换最多令其减少2,所以还要把它除以2。后来看了题解发现所有题解都是用的所有非0数的现在的位置到目标位置的曼哈顿距离和,这样的话一次交换最多令其减少1,这样应该是比较合适的。。
我一开始写的A*,(因为没有看懂ID-A*)A*需要面对map判重、heap维护,空间复杂度与时间复杂度相同,且挂了log的常数,时间空间都不占优。
后来终于看懂了ID-A*,ID-A*为什么不用判重?是因为我们考虑搜索树中的一个节点,如果它(假设我们做一个简单的剪枝、拒绝反着走到达当前状态的最后一步)被再次搜到,就意味着它至少又转了四下。也就是说一个点至多被搜 50413 ,即其常数约为13!也就是说这其实是比用平衡树判重快得多的。但是ID-A*是不能像A*一样每次取出预估最优的状态的,它只能通过调整上下来解决这个问题;这跟DFS与ID-DFS的区别还不一样,因为在一个预估较劣的状态可能会达到一个预估较优的状态。所以实际上ID-A*与A*的区别还是很大的,它实际上并不是A*的简单优化;它也并不一定比A*跑得更快,如果你的估价函数不是很准的话。。

其实这个题更有意思的地方,在于对于无解的判断。
我在网上并没有找到关于15数码的完整证明,不过有8数码的,但是我似乎并不能把它推广到15数码甚至是N*M数码。但是我前前后后研究了差不多有4个月,在这里将会给出一个严谨的证明。
命题:
在一个N*M( N>1,M>2 )的矩阵中,我们放入0~N*M-1的排列,称之为数码。
我们允许两种操作,一种是将0与其所在的格子左右相邻的格子里的数互换,一种是将0与其所在的格子上下相邻的格子里的数互换。

当M为奇数时,两种数码可以互达与两种数码【排列逆序对数(不算0)】奇偶性相同等价。
当M为偶数时,两种数码可以互达与两种数码【排列逆序对数(不算0)+0的纵坐标】奇偶性相同等价。

证明:
首先证明两种数码可以互达当且仅当……(后面那一串)
考虑0左右移动,显然不会改变不算0的排列的逆序对数;而上下移动的话,考虑增量,就相当于是有M-1个数,每个数∈{-1,1},问其和?在模2意义下,-1≡1,所以其实它们的和在模2意义下恒等于M-1.
那么接下来的事情就很显然了,其实这个玩意儿本身就是构造的,奇数的时候不用管它,反正M-1≡0(mod 2);偶数的时候,上下移动0的纵坐标就会+1或-1,所以再加上0的纵坐标的话,奇偶性就不会变了。

好!接下来就是真正的核心部分了!(我可是玩了很久才玩出规律的。。)
由于两个数码之间的变换是可逆的,现在所有的数码都分为了两类,所以如果我们能证明出所有的数码都能变换到两个数码之一,那么就好了!
我们选择将所有数码变换成类似这种形状:
[UVA10181]十五数码解题报告_第1张图片
但是我们该怎么做呢?
首先我们看一下前人对八数码的研究(因为由于排版造成的问题。。我进行了一些修改。):

转载自【水木清华BBS精华区】:http://www.cnw3.org/smth/AI/5/8/00000001.htm

发信人: YourMajesty (花痴~~~~小魔男), 信区: AI
标 题: 关于八码数问题有解与无解的证明(zz)
发信站: BBS 水木清华站 (Fri Nov 23 22:26:49 2001)

我们将九宫格按行排成一行共九个数(空格也占一个位置,在本文种,我用@表示空 格)。比如:
1 2 3
4 @ 5 => 1 2 3 4 @ 5 6 7 8 这样
6 7 8
,九宫格的每一种状态和上图的行之间是一一对应的。
为了证明上述定理,我想先对问题进行一下转化。我定义两种行序列的变换:一种是空格@和相邻的数对换,一种是空格@和前后隔两个数的数之间的对换(按:或是从这一行的行首到上一行的行末),前者对应着空格在九宫图中的左右移动,后者对应着空格在九宫图中的上下移动。
引理一:在上述的两种对换下,序列的奇偶性不改变。 这个引理很容易证明。
首先,相邻的对换肯定不改变奇偶性;其次,隔两格的对换也不改变奇偶性,它相当于三个数的轮换。这就说明了奇九宫图和偶九
宫图之间是互不可达的。
引理二:转化后行序列在上面定义的两种对换下的任意操作,可以转换成九宫图中 空格的合法变化。 这个引理也是比较容易证明的。我们只要证明如下的两种状态之
间是互达的:
1 2 3             1 2 3
4 5 6<=>4 5 @
@ 7 8           6 7 8
通过计算机搜索,可以发现上面两种状态之间的确是互达的。(按:我们其实也可以构造,使用与后面类似的方法。将数成对竖着排起来即可。由于非常简单而且我不会推广到高维。。这里略去不详细说明)从而,我们可以假定上面两种状态之间的转换可以用行序列中的两种邻对换来代替。
引理三:所有的奇状态可以转换为 @ 1 2 3 4 5 6 7 8, 所有的偶状态可以转换为 @ 2 1 3 4 5 6 7 8. 要证明这个引理,得分几个步骤。我的想法是先设法把8移到最后一个,然后8保持不动(注意,我们这里的不动只是形式上不动,但不管怎样,我们的每一个变换后,8还是保持在最后一个,余类似),再将7移到8之前,然后, 保持7和8不动,依次移动6,5,4,3,得到 * * * 3 4 5 6 7 8这里 * * *是 @ 1 2 的一个排列。到这里,我想要得到前面的两种状态之一是显然的了。下面,我说明,上面的想法是可以实现的。如下:
1. 对于 a b c @ ,我们可以将其中的任意一个移到
最后,并且对变换仅限于这四个位置上。显然,对于a,c是一步就可以做到的。对于b, 步骤如下: a b c @ -> @ b c a -> b @ c a -> b c @ a -> b c a @ -> @ c a b
2. 先把要移到最后位置的那个数移到最后四个位置之一,然后再将空格移到最后一个位置,用1的方法将待移动的数变换到最后一个位置。循环这样做即可。引理三证毕。
证完了这三个引理,定理的成立就是显然的了。首先,将奇偶性相同的两种状态都变换到上述两种标准状态之一,然后对其一去逆变换即可。

我们知道M=5与M=3是相似的,但是我感觉这个证明如果往M=5的情况推广的话,会遇到很大问题啊。因为这样的话最终它需要面临的就不再是1~2的排列了,而是1~4的排列。。然而我们却希望把它们划归到两种状态。

所以我们需要引入新的算子。
在研究15数码的时候,我发现了这样一件事情!其实我们可以把一行旋转,比如对于
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0
它可达
1 2 3 4
5 6 7 8
9 10 11 12
15 13 14 0
而3个数的排列只有6种,也就是说如果只有最后3个数不同而奇偶性,那么我们可以将其旋转使其相同。
这是非常重要的一点,下面我想具体地说明一下这是怎么实现的。
其基本思路就是把数成对成对地竖起来。
0、
1 2 3 4
5 0 6 7
1、
1 0 3 4
5 2 6 7
2、
5 1 3 4
0 2 6 7
3、
5 1 3 4
2 0 6 7
4、
5 0 3 4
2 1 6 7
5、
0 5 3 4
2 1 6 7
6、
2 5 3 4
0 1 6 7
7、
2 5 3 4
1 0 6 7
8、
2 5 3 4
1 6 0 7
9、
2 5 3 4
1 6 7 0
10、
2 5 3 0
1 6 7 4
11、
2 5 0 3
1 6 7 4
12、
2 0 5 3
1 6 7 4
13、
2 6 5 3
1 0 7 4
14、(看!它们都竖起来了!)
2 6 5 3
1 7 0 4
15、
2 6 0 3
1 7 5 4
16、
2 6 3 0
1 7 5 4
17、
2 6 3 4
1 7 5 0
18、
2 6 3 4
1 7 0 5
19、
2 6 3 4
1 0 7 5
20、
2 0 3 4
1 6 7 5
21、
0 2 3 4
1 6 7 5
22、
1 2 3 4
0 6 7 5
这其实意味着我们可以令一个数跳过两个数,我们称之为跳跃算子。
好嘞!现在让我们来试图划归一个N*M的数码。
这里,我们约定M>3,当M=3时。。是与八数码的证明相同的。(而我下面的证明却不能解决这种情况。。)
在下面的说明中,我们选择忽视0,因为0在左右移动的时候不会影响其他数的相对位置。
一、前N-2行。
首先我们对付前M-2列,这是易行的,只需要不断把空格绕道待操作数期望路线上就可以了;
对于最后两列,假如说这一行最后两个数是3、4,那么就先把4放到倒数第二列,然后把3放到下一行倒数第二列,然后把0放到这一行最后一列,然后把0左移、下移!
…… 4 0
…… 3
=>
…… 0 4
…… 3
=>
…… 3 4
…… 0
二、第N-1行。
注意到跳跃算子实际上是一行内的操作,如果我们规定一个数的坐标x(a)表示a的位置之前(一行内)有几个非0数,那么对于一个数而言,跳跃算子就是在把其坐标+2或-2的操作。
那么实际上我们就可以把一个数移动到任意一个坐标了!
因为我们可以首先对其不断使用跳跃算子,直到它的坐标与目标坐标之差的绝对值小于2;这时,如果它的坐标与目标坐标还差1的话,我们便可跳跃它周围的数,使之抵达目标坐标。这样做的正确性是显然的,因为使一个后面的数到前面去或是使一个前面的数到后面去会使当前的数的坐标+1或-1.
但是这样做是有条件的,它的条件是这一行至少有三个数(这个很显然吧)。

所以,我们顺序前M-2列,把待操作的数移到最后一行,然后使用跳跃算子把它移到期望位置的下面,然后把0移到它上面,然后把0移下来即可。
然后使用类似的方法,把倒数第一个数移到倒数第二个位置上,把倒数第二个数移到最后一排的最后,然后把0移到最后(这样它就在倒数第二个位置上了),然后把0移上去,向对付前几行那样转一下即可。
这样我们就只需处理最后一行了。
三、第N行。
注意我们刚刚得出的结论,使用跳跃算子可以把一个数移到任意坐标!而它的
那么接下来该做的事情就几乎是显而易见了,假如说最后我们应该把M-1~1排出来(当然这是不可能的,不过我们可以把我们需要做的排列完全等价地映射到这上面去),那么我们就可以先把M-1放到第一个位置,然后保持其不动,把M-2放到第2个位置。。依次操作,直到——直到还剩两个数!而两个数的排列只有两种!
于是,我们便可把所有排列映射到两个排列之一,原命题得证!
命题:
在一个N*M( N>1,M>2 )的矩阵中,我们放入0~N*M-1的排列,称之为数码。
我们允许两种操作,一种是将0与其所在的格子左右相邻的格子里的数互换,一种是将0与其所在的格子上下相邻的格子里的数互换。

当M为奇数时,两种数码可以互达与两种数码【排列逆序对数(不算0)】奇偶性相同等价。
当M为偶数时,两种数码可以互达与两种数码【排列逆序对数(不算0)+0的纵坐标】奇偶性相同等价。

让我们再来做一些有趣的扩展!
考虑对于a*b*c(a>2,b>2,c>2)的长方体,它里面有0~a*b*c-1,是一个像平面数码一样的立体数码,然后0在里面到处乱转。那么它的有解无解情况又该是如何呢?
显然结论是,若以a为纵坐标长度,b为横坐标长度,c为高度,考虑把每一个平面顺次展开的排列的逆序对,则我们加上0的纵坐标当且仅当b为偶数,我们加上0的高度当且仅当a*b为偶数。
证明的话也是容易的,考虑在z轴上移动会有a*b-1个+1或-1,在y轴上移动会有b-1个,在x轴上移动不会改变逆序对。
我们从上到下顺次处理每一个平面,大可直接把当前平面位置需要的数直接移到它z轴-1的位置,然后让0从x轴-1或y轴-1的位置过去,就可以了;然后处理一下最后两个数,先把倒数第一个数放到倒数第二个数的位置,然后把倒数第二个数放到它z轴-1的位置,然后让0上去转一圈就好了。这样还剩最后一个平面,就是正常的平面数码问题了。(这样看来。。其实立体数码问题要比平面数码问题简单多啊!)

回顾一下我是怎么解决十五数码问题的,对于有解的情况,我选择了A*和ID-A*算法,我发现:
对于特殊点,我可以对它做特殊处理往往会有更好的效果!(我那傻逼的估价函数啊。。)
对于无解的情况,我首先得到了结论。然后我试图证明它,在百思不得其解以后参阅了八数码问题的无解证明,并受之启发。
应用了可逆性、归一法、排列的映射等多种有趣的性质!终于完成了N*M数码的证明。

你可能感兴趣的:(堆,DFS,平衡树)