一直试图解决Project Euler problem 15(需要查看网站上的图表来理解它)。
我知道有数学方法可以解决这个问题,但我有兴趣找到一种使用直接探索的方法。我的想法是从左上角开始,然后使用递归函数来分支每个可能的选项(即向右或向下,只要你不在边缘),返回一个可能的路径列表。 / p>
我的问题是我没有看到如何处理你可以分支的情况。到目前为止,我想出了以下内容......
let findPaths n =
let rec path n x y p =
if x < 1 || x > n || y < 1 || y > n then failwith (sprintf "Invalid position (%d, %d)" x y)
match (x, y) with
| (_, _) when x = n && y = n ->
printfn "Done"
(x, y)::p
| (_, _) when x = n && y < n ->
printfn "At (%d, %d), can only move down" x y
(x, y)::path n x (y + 1) p
| (_, _) when x < n && y = n ->
printfn "At (%d, %d), can only move right" x y
(x, y)::path n (x + 1) y p
| (_, _) ->
printfn "At (%d, %d), can move right or down" x y
(x, y)::path n (x + 1) y p
path n 1 1 []
printfn就在那里,所以我可以看到它是如何通过网格的。
这将找到一条路径,即向右移动直到它到达边缘,然后向下移动到网格的末尾。
我想做的是让最后一个案例探索两种可能性,返回一个路径列表。这样,它最终将返回所有路径。我不知道怎么做。
任何想法?我甚至可以这样做吗?如果没有,你还会怎么解决这个问题?同样,我正在寻找一种探索网格的解决方案,因此理论上可以在缺少边缘的网格上使用,其中简单的数学方法不起作用。
答案 0 :(得分:4)
我发现一个非常好的解决方案是更改path
函数,使其返回一系列路径而不是单个路径。这可以通过将正文包裹在seq { .. }
内并使用yield
关键字生成结果来完成。好消息是它不会非常改变您的代码:
let findPaths n =
let rec path n x y p = seq {
if x < 1 || x > n || y < 1 || y > n then failwith (sprintf "Invalid position (%d, %d)" x y)
match (x, y) with
| (_, _) when x = n && y = n ->
printfn "Done"
yield (x, y)::p
| (_, _) when x = n && y < n ->
printfn "At (%d, %d), can only move down" x y
for subPath in path n x (y + 1) p do
yield (x, y)::subPath
| (_, _) when x < n && y = n ->
printfn "At (%d, %d), can only move right" x y
for subPath in path n (x + 1) y p do
yield (x, y)::subPath
| (_, _) ->
printfn "At (%d, %d), can move right or down" x y
for subPath in path n x (y + 1) p do
yield (x, y)::subPath
for subPath in path n (x + 1) y p do
yield (x, y)::subPath }
path n 1 1 []
一个棘手的问题是,现在需要在递归调用path
时迭代所有可能的子路径 - 然后在每个子路径的开头附加当前的(x, y)
。 / p>
有一些重复,因为您现在需要代码在两个不同的位置向下移动/向右移动。我认为使用普通的if
实际上更容易编写 - 当你没有模式匹配时,使用match
没有多大好处:
let findPaths n =
let rec path n x y p = seq {
if x < 1 || x > n || y < 1 || y > n then
failwith (sprintf "Invalid position (%d, %d)" x y)
if x = n && y = n then yield (x, y)::p
if y < n then
for subPath in path n x (y + 1) p do
yield (x, y)::subPath
if x < n then
for subPath in path n (x + 1) y p do
yield (x, y)::subPath }
path n 1 1 []
另外,我认为你开始使用一个使用累加器参数p
的解决方案,但是你放弃了它,而不是在你去的时候构建路径,你正在构建它,因为你从递归调用。使用p
可以使代码更加出色:
let findPaths n =
let rec path n x y p = seq {
if x < 1 || x > n || y < 1 || y > n then
failwith (sprintf "Invalid position (%d, %d)" x y)
if x = n && y = n then yield List.rev ((x, y)::p)
if y < n then
yield! path n x (y + 1) ((x, y)::p)
if x < n then
yield! path n (x + 1) y ((x, y)::p) }
path n 1 1 []