只有思路还没有实践
这个题一眼看上去就是典型的状态空间搜索问题,a,b,c的每一个不同摆放位置都是一个状态,每一个状态都可以看成无权有向无环状图上的一个节点。又是找移动步数最少的走法,很自然就会想到用BFS。
细想上去,这个题稍微麻烦的地方是,如何从一个状态扩展到下一个状态。每个状态有2个或者3个字母需要考虑。 本以为只要考虑先尝试移动a,然后b,最后考虑移动c, 每个字母在四个方向上各尝试移动一步就可以了,那么对于一个状态,需要尝试的下一个状态数目只是4*4*4。其实不对,第一步移动a和第一步移动c,形成的状态可能完全是不一样的,那么需要尝试的顺序不止是a,b,c这么简单,这三个字母的全排列都是可能需要尝试的顺序。那么abc的排列个数一共有6个, {a,b,c},{a,c,b},{b,a,c},...etc. 那么需要尝试的下一个状态个数是6*4*4*4个。
这样其实还不完整,因为除了移动顺序不同,可能产生的状态不同以外, 还需要考虑某一两个字母不动的情况,比如只有c移动了一步,a和b完全不动, 这也会产生一种新状态。所以可能的移动顺序并不是abc的全排列,而是除空集以外所有子集的全排列,如{a},{b},{c},{a,b},{b,a}...etc,这样一来要尝试的顺序就变得更多。abc的子集有{a},{b},{c},{a,b},{b,c},{a,c},{a,b,c},他们的全排列一共15个,也就是说最后要尝试的状态个数是15*4*4*4共960种!当然这中间大多数是不合法的,最后真正能压入BFS队列的可能很少。
那么重960种候选状态中那些是合法的?需要做如下判断
1)字母不能摆出图形的边界
2)字母不能摆在障碍上
3)字母不能摆在另一个字母上
4)形成的状态不能是以前处理过的状态
这四项判断应该能筛除掉掉大部分的备选状态。
要进行这四项判断,必须有一个数据结构来保存原始的底图,用二维数组来标明,哪个位置有障碍,哪个位置可以摆放。这个图的长宽要保留下来做是否出界的判断。还要有一个数据结构用来保留已经处理过的状态,确保不重复处理同一个状态,这个数据结构应该是个hash的结构,现在还没想好怎么做。
题目要求打印需要最少的步数,那么每一个状态中应该有一个数据用来保存这个状态是在第几步产生的。查询的结束条件有两个:一个是找到的状态和目标状态完全一致,那么查找成功;一个是所有的状态全部处理完毕,BFS队列全空,说明没有一条路径可以到达目标状态。
原题中要移动的字母也可能只有a,b两个,它的子集和排列都少很多,要尝试的下一个状态也少很多,但是这个变化稍稍增加了题目的复杂度。还好这题只是要打印步数,如果要打印路径,那就更麻烦,在每一个已处理过的状态中要有一个指针指向上一个状态。最后在倒着把这个列表打出来。一个状态可以扩展出很多子状态,但是每个子状态只有一个父状态。
这个题的思路就是这样的,标准的状态空间搜索,就是条件太多,写起来是个力气活儿,调起来更是不敢想, 现在有点懒得写,不知道还有没有更好的方法,要不要真的这么麻烦?