使用haskell打印所有可能的路径

时间:2012-12-14 04:12:32

标签: algorithm haskell

我需要编写一个程序来绘制给定矩阵中的所有可能路径,这些路径可以通过仅向左,向右和向上移动来获得。 一个人不应该多次越过同一个位置。另请注意,在特定路径上,我们可能会或可能不会在所有可能的方向上使用运动。

路径将从矩阵的左下角开始,并将到达右上角。 以下符号用于表示当前位置的运动方向:

 +---+
 | > |  right
 +---+
 +---+
 | ^ |  up
 +---+
 +---+
 | < |  left
 +---+

在最终位置使用符号*来表示路径的结束。

示例:

对于5x8矩阵,使用向左,向右和向上的方向,下面显示了2个不同的路径。

路径1:

 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   |   |   | * |
 +---+---+---+---+---+---+---+---+
 |   |   | > | > | > | > | > | ^ |
 +---+---+---+---+---+---+---+---+
 |   |   | ^ | < | < |   |   |   |
 +---+---+---+---+---+---+---+---+
 |   | > | > | > | ^ |   |   |   |
 +---+---+---+---+---+---+---+---+
 | > | ^ |   |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+

路径2

 +---+---+---+---+---+---+---+---+
 |   |   |   | > | > | > | > | * |
 +---+---+---+---+---+---+---+---+
 |   |   |   | ^ | < | < |   |   |
 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   | ^ |   |   |
 +---+---+---+---+---+---+---+---+
 |   |   | > | > | > | ^ |   |   |
 +---+---+---+---+---+---+---+---+
 | > | > | ^ |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+

任何人都可以帮我吗?

我试图解决使用列表问题。我很快意识到我正在制造灾难。这是我试过的代码。

 solution x y = travel (1,1) (x,y) 
 travelRight (x,y) = zip [1..x] [1,1..] ++ [(x,y)] 
 travelUp (x,y) = zip [1,1..] [1..y] ++ [(x,y)]
 minPaths = [[(1,1),(2,1),(2,2)],[(1,1),(1,2),(2,2)]]

 travel startpos (x,y) = rt (x,y) ++ up (x,y)

 rt (x,y) | odd y = map (++[(x,y)]) (furtherRight (3,2) (x,2) minPaths)
          | otherwise = furtherRight (3,2) (x,2) minPaths
 up (x,y) | odd x = map (++[(x,y)]) (furtherUp (2,3) (2,y) minPaths)
          | otherwise = furtherUp (2,3) (2,y) minPaths

 furtherRight currpos endpos paths | currpos == endpos = (travelRight currpos) : map (++[currpos]) paths
                                   | otherwise = furtherRight (nextRight currpos) endpos ((travelRight currpos) : (map (++[currpos]) paths))
 nextRight (x,y) = (x+1,y)

 furtherUp currpos endpos paths | currpos == endpos = (travelUp currpos) : map (++[currpos]) paths
                                | otherwise = furtherUp (nextUp currpos) endpos ((travelUp currpos) : (map(++[currpos]) paths))
 nextUp (x,y) = (x,y+1)

 identify lst = map (map iden) lst
 iden (x,y) = (x,y,1)


 arrows lst = map mydir lst
 mydir (ele:[]) = "*"
 mydir ((x1,y1):(x2,y2):lst) | x1==x2 = '>' : mydir ((x2,y2):lst)
                             | otherwise = '^' : mydir ((x2,y2):lst)

 surroundBox lst = map (map createBox) lst
 bar = "+    -+"
 mid x = "| "++ [x] ++" |"
 createBox chr = bar ++ "\n" ++ mid chr ++ "\n" ++ bar ++ "\n"

3 个答案:

答案 0 :(得分:3)

这种ASCII网格比启发更容易混淆。让我描述一种更好的方式来表示每条可能的路径。

每个非顶行将只有一个带UP的单元格。我声称,一旦选择了每个UP单元,就可以确定LEFT和RIGHT和EMPTY单元。我声称每个非顶行中的所有可能单元可以在所有组合中都是UP。

因此,每个路径与确定UP单元格的范围(1..columns)中的(行-1)长度数列表同构。因此,允许的路径数为列^(行-1),并且以这种格式枚举可能的路径应该很容易。

然后你可以制作一台将这种格式转换为ASCII艺术品的打印机。这可能很烦人,具体取决于技能水平。

答案 1 :(得分:1)

看起来像是一个功课,所以我会尝试提供足够的提示

  • 首先尝试填充从单元格到目标的路径数。

所以

 +---+---+---+---+---+---+---+---+
 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | * |
 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+
 |   |   |   |   |   |   |   |   |
 +---+---+---+---+---+---+---+---+

这里要注意的是从顶层的单元格到*总会有一条路径。

  • 同一行中单元格的可能路径数相同。您可以实现这一点,因为所有路径最终都必须向上移动,因为没有向下操作,因此在任何路径中,当前行上的任何单元格都可以访问当前行上方的单元格。

  • 您可以感觉到当前单元格中所有可能的路径与左侧,右侧和上方单元格的可能路径有关。但正如我们所知,我们可以从一行中的一个单元格中找到所有可能的路径,而其余单元格的可能路径将是同一行中的一些移动,后跟该单元格中可能路径的后缀。

也许我会举个例子

 +---+---+---+
 | 1 | 1 | * | 
 +---+---+---+
 |   |   |   |  
 +---+---+---+
 |   |   |   |   
 +---+---+---+

您知道第一行中单元格的所有可能路径。您需要在第二行中找到相同的内容。因此,一个好的策略是为最正确的细胞做到这一点

 +---+---+---+
 | > | > | * | 
 +---+---+---+
 | ^ | < | < |  
 +---+---+---+
 |   |   |   |   
 +---+---+---+

 +---+---+---+
 |   | > | * | 
 +---+---+---+
 |   | ^ | < |  
 +---+---+---+
 |   |   |   |   
 +---+---+---+

 +---+---+---+
 |   |   | * | 
 +---+---+---+
 |   |   | ^ |  
 +---+---+---+
 |   |   |   |   
 +---+---+---+

现在,正如我之前所说的那样,使用这些细胞可以轻松找到同一行中剩余的细胞。

最后,如果您有m X n矩阵,则从左下角到右上角的路径数将为n^(m-1)

另一种方式

这种方式不是很优化,但易于实现。考虑m X n网格

  • 找出最长的路径。您不需要<>^的确切路径。 您可以根据mn找到直接公式。

喜欢

 ^ = m - 1
 < = (n-1) * floor((m-1)/2) 
 > = (n-1) * (floor((m-1)/2) + 1)
  • 任何有效路径都将是您可以进行详尽搜索的排列的前缀。使用permutations中的Data.List获取所有可能的排列。然后创建一个给定路径的函数从中剥离有效路径。将其映射到排列列表上并删除重复项。需要注意的是路径将是从排列中获得的前缀,因此对于相同的路径可以有多个排列。

答案 2 :(得分:0)

您可以创建该矩阵并定义&#34;字段&#34;?即使你不能(给出一个特定的矩阵),你也可以将[(Int, Int)]矩阵(这对于这种任务听起来合理)映射到你自己的表示中。

由于您没有指定您的技能水平,我希望您不要介意我建议您首先尝试创建某种网格,以便有所作为:< / p>

data Status = Free | Left | Right | Up
    deriving (Read, Show, Eq)
type Position = (Int, Int)
type Field = (Position, Status)
type Grid = [Field]

grid :: Grid
grid = [((x, y), stat) | x <- [1..10], y <- [1..10], let stat = Free]

当然还有其他方法可以实现这一目标。之后,您可以定义一些动作,将Position映射到Grid索引,将Status es映射到可打印字符...尝试摆弄它,您可能会得到一些想法。