作为一个学校项目,我必须递归地使用回溯方法在迷宫中找到求解路径,对于线性问题,我通常没有使用递归来求解算法的问题,但是当涉及到多个选择/路径时,我不知道如何只找到一种解决方案。
问题参数:
使用的语言:
代码输出:
████████████████████
█ █ ██
█ ███ ███ █████ █ ██
█ █ █ █ █ ██
█ █ █ █ █ █ █ ███ ██
█ █Sxxxx █ ██
█ █████ █ █x███ █ ██
█ █ █xxx█ ██
█ █ █ ███████x██████
█ █ █ █ █x ██
█ █ ███ █ █ █x███ ██
█ █ █ █ x█ ██
█ ███ ███ █ █x█ █ ██
█ x█ ██
███ █████████x███ ██
███ █ █xxx█ █ ██
███ █ █ █ █x███ █ ██
███ █ █xxx█ ██
█████████x██████████
█████████E██████████
#:墙壁
:路径
E:终点
S:起点
部分代码:
let rec dfs(x,y,path,visited) =
let rec checkVisited point visited =
match visited with
| [] -> false
| (x,y)::xs -> if point = (x,y) then true else checkVisited point xs
let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
for point in adjacents do
if point = this.endPoint then
this.solutionPath <- path
else
if checkVisited point visited = false && this.checkPoint point && this.isWall point = false then
dfs(fst(point),snd(point),(path@[point]),(visited@[(x,y)]))
这是在迷宫中搜索解决方案的另一种方法(优化了动作)
let rec dfs(x,y,path) =
// setting the point in the matrix visited (setting it to 'false')
matrix.[y].[x] <- (fst(matrix.[y].[x]),false)
// getting the adjacents of the point
let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
// iterate the adjacents
for (x1,y1) in adjacents do
// if the adjacent is the end point set the soultion path
if (x1,y1) = this.endPoint then
this.solutionPath <- path
else
// else check if the point is in the matrix and is not yet visited
if this.checkPoint(x1,y1) && snd(matrix.[y1].[x1]) <> false && this.isWall(x1,y1) = false then
// execute recursively the method in the point and add the current poisition to the path
dfs(x1,y1,((x1,y1)::path))
dfs(x,y,[])
我做到了!如果您在执行此操作时遇到任何麻烦,我将为您提供帮助(甚至使用其他语言)!
答案 0 :(得分:7)
您当前的方法看起来还可以。但是,由于您要进行深度优先搜索,所以您遇到的主要问题是,没有什么可以阻止您像尝试[(1,1);(1,2);(1,1);...]
这样的无限长路径而陷入困境,而不是去寻找更有效率的路径。为避免这种情况,您可以扫描路径以查看建议的下一个点是否已存在(这在列表的长度上最多花费时间是线性的,这对于较小的问题大小可能是合适的),或者传递访问点集作为递归函数的附加参数(应该允许更快的成员资格查询)。
您遇到的另一个问题是,您没有任何方法可以合并不同分支的结果。一种简单的方法是将内部函数的返回类型更改为选项类型,并从顶部的Some(path)
返回if
并将其他内容重写为类似的内容
[x, y+1
x, y-1
x+1, y
x-1, y]
|> List.tryPick (fun (x',y') -> if this.checkPoint(x',y') then
sol(x', y', (x,y)::path)
else None)
这是递归地依次尝试每个可能的方向,并返回第一个成功的方向。由于这是深度优先的搜索,因此不一定会返回最短路径。您还可以轻松地创建一个变体,该变体返回所有可能路径的列表,而不是使用选项(最大的更改是使用List.collect
而不是List.tryPick
),在这种情况下,您可以选择最短的解决方案如果需要,可以从列表中进行选择,尽管这样做会进行很多中间计算。
更复杂的更改是切换到广度优先搜索而不是深度优先搜索,这将使您很容易返回最短路径。从概念上讲,这是一种方法,以追踪到达所有“可见”点的最短路径(仅从[S,[]]
开始,以及尚未探索其子项的一组点(再次以只要[S]
),然后,只要有要探索的点,就收集他们所有的不同子级;对于每个尚不知道路径的子级,将其添加并放入下一组孩子们去探索。