# 八数码

525人阅读 评论(0)
﻿﻿

Eight

Time Limit: 1000MS    Memory Limit: 65536K  Special Judge

Description

The 15-puzzle has been aroundfor over 100 years; even if you don't know it by that name, you've seen it. Itis constructed with 15 sliding tiles, each with a number from 1 to 15 on it,and all packed into a 4 by 4 frame with one tile missing. Let's call themissing tile 'x'; the object of the puzzle is to arrange the tiles so that theyare ordered as:

1 2  3  4

5 6  7  8

9  10 1112

13 14 15 x

where the only legal operationis to exchange 'x' with one of the tiles with which it shares an edge. As anexample, the following sequence of moves solves a slightly scrambled puzzle:

1 2  3  4    1  2 3  4    1 2  3  4   1  2  3  4

5 6  7  8   5  6  7 8    5  6 7  8    5 6  7  8

9  x 1012    9 10  x 12   9 10 11 12    9 10 11 12

13 14 11 15   13 14 11 15  13 14  x 15   13 14 15 x

r->           d->           r->

The letters in the previousrow indicate which neighbor of the 'x' tile is swapped with the 'x' tile ateach step; legal values are 'r','l','u' and 'd', for right, left, up, and down,respectively.

Not all puzzles can be solved;in 1870, a man named Sam Loyd was famous for distributing an unsolvable versionof the puzzle, and

frustrating many people. Infact, all you have to do to make a regular puzzle into an unsolvable one is toswap two tiles (not counting the missing 'x' tile, of course).

In this problem, you willwrite a program for solving the less well-known 8-puzzle, composed of tiles ona three by three arrangement.

Input

You will receive a descriptionof a configuration of the 8 puzzle. The description is just a list of the tilesin their initial positions, with the rows listed from top to bottom, and thetiles listed from left to right within a row, where the tiles are representedby numbers 1 to 8, plus 'x'. For example, this puzzle

1 2  3

x 4  6

7 5  8

is described by this list:

1 2 3 x 4 6 7 5 8

Output

You will print to standardoutput either the word unsolvable'', if the puzzle has no solution, or astring consisting entirely of the letters 'r', 'l', 'u' and 'd' that describesa series of moves that produce a solution. The string should include no spacesand start at the beginning of the line.

Sample Input

2 3  4  1 5  x  7 6  8

Sample Output

ullddrurdllurdruldr

这个题目是SpecialJudge，任意找出一组移法就行，但是很多时候，我们需要找到步数最少的移法，所以，这里以步数最少的移法为目的。真正优化这个问题涉及到很多，比如A*、全排列哈希、堆优化等。一境界一代码，咱们一个境界一个境界走，慢慢优化这个经典问题，当然，我不是那么无聊，不会把所有境界都列出代码……

境界一、 暴力广搜+STL

开始的时候，自然考虑用最直观的广搜，因为状态最多不超过40万，计算机还是可以接受的，由于广搜需要记录状态，并且需要判重，所以可以每次图的状态转换为一个字符串，然后存储在stl中的容器set中，通过set的特殊功能进行判重，由于set的内部实现是红黑树，每次插入或者查找的复杂度为Log(n)，所以，如果整个算法遍历了所有状态，所需要的复杂度为n*Log(n)，在百万左右，可以被计算机接受，由于对string操作比较费时，加上stl全面性导致 速度不够快，所以计算比较费时，这样的代码只能保证在10秒内解决任何问题。但，明显效率不够高。POJ上要求是1秒，无法通过。

境界二、广搜+哈希

考虑到费时主要在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是解决单问题的上限。当然，还有一个修改的小技巧，就是逆序对数不会改变，通过这个，可以直接判断某输入是否有可行解。由于对于单组最坏情况的输入，此种优化不会起作用，所以不会减少单组输入的时间上限。

境界三、广搜+哈希+打表

好，问题可以在200—300ms间解决，可是，这里我们注 意到一个问题，最坏情况下，可能搜索了所有可达状态，都无法找到解。如果这个题目有多次输入的话，每次都需要大量的计算。其实，这里可以从反方向考虑下，从最终需要的状态，比如是POJ 1077需要的那种情况，反着走，可达的情况是固定的。可以用上面说的那种相应的Hash的方法，找到所有可达状态对应的值，用一个bool型的表，将可达状态的相应值打表记录，用“境界三”相似的方法记录路径，打入表中。然后，一次打表结束后，每次输入，直接调用结果！这样，无论输入多少种情况，一次成功，后面在O（1）的时间中就能得到结果！这样，对于ZOJ的多组输入，有致命的帮助！

境界四、双向广搜+哈希
Hash，不再赘述，现在，我们来进行进一步的优化，为了减少状态的膨胀，自然而然的想到了双向广搜，从输入状态点和目标状态1 2 3 4 5 6 7 8 9同时开始搜索，当某方向遇到另一个方向搜索过的状态的时候，则搜索成功，两个方向对接，得到最后结果，如果某方向遍历彻底，仍然没有碰上另一方向，则无法完成。

境界五、A*+哈希+简单估价函数
用到广搜，就可以想到能用经典的A*解决，用深度作为g(n)，剩下的自然是启发函数了。对于八数码，启发函数可以用两种状态不同数字的数目。接下来就是A*的套路，A*的具体思想不再赘述，因为人工智能课本肯定比我讲的清楚。但是必须得注意到，A*需要满足两个条件：

1.h(n)>h'(n),h'(n)为从当前节点到目标点的实际的最优代价值。

2.每次扩展的节点的f值大于等于父节点的f值小。

境界六、A*+哈希+曼哈顿距离

A*的核心在启发函数上，境界五若想再提升，先想到的是启发函数。这里，曼哈顿距离可以用来作为我们的启发函数。曼哈顿距离听起来神神秘秘，其实不过是“绝对轴距总和”，用到八数码上，相当与将所有数字归位需要的最少移动次数总和。作为启发函数，自然需要满足“境界五”提到的那两个条件。现在来看这个曼哈顿距离，第一个条件自然满足。对于第二个，因为空格被我们剥离出去，所以交换的时候只关心交换的那个数字，它至多向目标前进1，而深度作为g每次是增加1的，这样g+h至少和原来相等，那么，第二个条件也满足了。A*可用了，而且，有了个更加优化的启发函数。

境界七、A*+哈希+曼哈顿距离+小顶堆

经过上面优化后，我们发现了A*也有些鸡肋的地方，因为需要每次找到所谓Open表中f最小的元素，如果每次排序，那么排序的工作量可以说是很大的，即使是快排，程序也不够快！这里，可以想到，由于需要动态的添加元素，动态的得到程序的最小值，我们可以维护一个小顶堆，这样的效果就是。每次取最小元素的时候，不是用一个n*Log(n)的排序，而是用log(n)的查找和调整堆，好，算法又向前迈进了一大步。

境界八、IDA*+曼哈顿距离

IDA*即迭代加深的A*搜索，实现代码是最简练的，无须状态判重，无需估价排序。那么就用不到哈希表，堆上也不必应用，空间需求变的超级少。效率上，应用了曼哈顿距离。同时可以根据深度和h值，在找最优解的时候，对超过目前最优解的地方进行剪枝，这可以导致搜索深度的急剧减少，所以，这，是一个致命的剪枝！因此，IDA*大部分时候比A*还要快，可以说是A*的一个优化版本！

个人资料
等级：
访问量： 529
积分： 8
排名： 258万+
文章分类
文章存档
阅读排行