## 【HDOJ1043】八数码的八境界 转

沉默的大绵羊

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秒，无法通过，第一次的代码见Code1.cpp。

境界二、广搜+哈希

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

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

好，问题可以在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)的查找和调整堆，好，算法又向前迈进了一大步。这里，我们终于要给出代码了，代码参见Code3.Cpp。

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

IDA*即迭代加深的A*搜索，实现代码是最简练的，无须状态判重，无需估价排序。那么就用不到哈希表，堆上也不必应用，空间需求变的超级少。效率上，应用了曼哈顿距离。同时可以根据深度和h值，在找最优解的时候，对超过目前最优解的地方进行剪枝，这可以导致搜索深度的急剧减少，所以，这，是一个致命的剪枝！因此，IDA*大部分时候比A*还要快，可以说是A*的一个优化版本！代码参见Code4.cpp。到此，到了我的境界八……请老师再将境界进行升级……还有，前文中也许会有很多疏漏之处，望老师给以修正。

### 沉默的大绵羊

·八数码简介 八数码问题也称为九宫问题。在3×3的棋盘，摆有八个棋子，每一个棋子上标有1至8的某一数字，不同棋子上标的数字不同样。棋盘上另一个空格，与空格相邻的棋子能够移到空格中。要...

e_one
2017/04/08
0
0
A搜索算法（python）之八数码问题

2018/07/13
0
0
8数码问题BFS实现---java

Lee24
2012/08/23
0
0
【DM原著向·改】【武嘉】闺蜜（九）

“怎么样？”男生躺在床上，枕着自己的手臂，懒洋洋地发问。 女生伸个懒腰，不再看电脑，转过头来，认真回答道：“呐，阿武，在麻鹰兽进化成忍者兽之前，是不是还应该有点什么？” 高石武坐起...

yigoh
2017/09/18
0
0
【DM原著向·改】【武嘉】闺蜜（八）

“那么，大家好好休息一下，我们明天就要去海边玩了！”太刀川美美举着右手，一如既往地很有活力。 高石武转头看了八神嘉儿一眼，抬手招呼一下，希望通过后视镜吸引到坐在副驾驶席上那位长发...

yigoh
2017/09/18
0
0

57分钟前
4
0
SpringBoot引入第三方jar包或本地jar包的处理方式

2
0

yangjianzhou

2
0

3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字： 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》（李继宏译），第一疑问就是全书即没提到“月亮”，也没提到“六便士”。那这书名又与内容有什么...

3
0