SGOI7《仓库管理员》
http://www.mydrs.org 3/23/2002 大榕树
这道题实际上就是大家熟知的"推箱子"游戏,它是从经典的迷宫最短路问题发展而来的,可采用广度优先搜索作为基本方法解题。所不同的是,迷宫问题只要前方没有障碍物便可以朝这个方向前进;而这道题还要求Sam能够"推动"箱子。也就是说,当我们在搜索中扩展一个节点的时候,还要检查Sam能否从当前位置到达和箱子前进方向相反的那个邻位上。
在算法的实现中,我们不妨定义两个表示方向的常量: cfx:array[1..4] of shortint=(1,0,-1,0); cfy:array[1..4] of shortint=(0,1,0,-1);
如果用(cx,cy)表示箱子的当前位置,要判断箱子能否前进到方格(cx+cfx[i],cy+cfy[i]),只要检查Sam能够从他的当前位置到达(cx-cfx[i],cy-cfy[i]),Sam的当前位置实际上也就是箱子上次被推动时所在的位置(队列中当前节点的父节点标记的位置)。另外,为了尽快确定Sam能否达到要求的位置,检查过程也应采用广度优先搜索,并在扩展到目标位置后立即退出,这将大大缩短检查的时间。
明确的以上几点后,就不难写出程序的框架:它是两个嵌套的BFS,主过程的BFS搜索箱子的路径,在每次入队前调用另一个BFS寻找Sam的可达区域,确定改节点是否应该入队。
这道题还有一个注意点:主BFS中标记一个方格是否被访问过,不能只记录方格的坐标,因为同一个方格有可能被走过不止一次。例如下面这个数据:

Sam(红点)可以把箱子向前推两格后,绕道箱子后面,从原路便可将其推回目标位置(黄色方块)。这样,就有一些方格被访问了两次,而如果错误地做了访问标记,强制每个方格只能访问一次,则可能输出无解。然而,虽然同一个方格可以被多次访问,但从同一个方向上最多只能被访问一次。于是我们可以用一个三位数组used[x,y,d]标记是否从方向d上访问过方格(x,y),通过扩大状态表示解决上述问题。
此外,搜索过程中应注意地图边界的处理,因为所给的数据不一定是封闭的。特殊情况的拦截也是本题测试点之一:如果所给的箱子位置已经在目标位置上,而Sam却无法接近箱子,就不该输出无解(例如输入数据7)。
|