使用带有一组规则的递归查找最长路径

时间:2019-05-13 06:54:38

标签: python recursion path-finding

有一点背景知识,我根本不是计算机科学家或程序员(我在大学里学习过一些python的研究过的物理学)。我的问题是通过具有给定规则集的矩阵找到最长的路径。矩阵示例如下所示:

   [0, 0, 0, 0, 0],
   [0, 1, 0, 0, 0],
   [1, 1, 1, 0, 0],
   [0, 0, 0, 0, 0],
   [1, 0, 0, 1, 0],

规则如下:

  1. 从给定的“ 1”位置开始,这些是有效位置。

  2. 每次跳转都必须移至同一行或同一列上的另一个有效位置(可以跳过“ 0”)。

  3. 连续跳跃不能在同一方向上(水平/垂直),除非从对角线位置跳跃。

  4. 任何位置都不能使用两次。

示例矩阵上的有效路径如下:

(5,4),(5,1),(3,1),(3,3),(3,2),(2,2)

由于规则3而导致的无效路径如下:

(3,1),(3,2),(3,3)

可能会出现以下

(3,1),(3,3),(3,2)

尽管我对python有一定的经验,但我从未尝试过递归(我很确定这是解决此问题的方法),而且我似乎无法在我的水平上找到任何在线帮助。

2 个答案:

答案 0 :(得分:1)

有几种解决方案。我建议先将网格转换为更面向对象的结构,即无向图,其中的节点在输入中为1。

然后,我将在该图中区分三种边缘:

  • 端点之一在对角线上(“特殊”边缘)的人
  • 以上条件不正确,但节点在同一行中的那些
  • 以上情况不正确,但节点位于同一列中的

在重复进行时,您将始终考虑特殊的边缘,除此之外,还要考虑其他两组边缘之一中的边缘(基于以前的方向)。

这是一个实现:

class Node:
    def __init__(self, y, x, size):
        self.x = x
        self.y = y
        self.coord = (y, x)
        self.diagonal = x == y or size - 1 - y
        # Separate lists of neighbors: vertical, horizontal. 
        # Third list is for when this node or neighbor is on diagonal
        self.neighbors = [[], [], []] 

    def addNeighbor(self, node, direction):
        self.neighbors[direction].append(node)

class Maze:
    def __init__(self, grid):
        def addedge(a, b):
            direction = 2 if a.diagonal or b.diagonal else int(a.x == b.x)
            a.addNeighbor(b, direction)
            b.addNeighbor(a, direction)

        # alternative grid having Node references:
        self.nodes = [[None] * len(grid) for _ in grid] 
        colNodes = [[] for _ in grid]
        for y, row in enumerate(grid):
            rowNodes = []
            for x, cell in enumerate(row):
                if cell: # only create nodes for when there is a 1 in the grid
                    node = Node(y, x, len(grid))
                    for neighbor in rowNodes + colNodes[x]:
                        addedge(node, neighbor)
                    rowNodes.append(node)
                    colNodes[x].append(node)
                    self.nodes[y][x] = node

    def findpath(self, y, x):
        def recur(node, neighbors):
            visited.add(node)
            longest = [node.coord]
            # always visit "special" neighbors 
            #   (i.e. those on diagonal or all vert/horiz when node is on diagonal)
            for neighbor in node.neighbors[2] + neighbors:
                if not neighbor in visited:
                    # toggle direction when going further
                    path = recur(neighbor, node.neighbors[1-int(node.x == neighbor.x)])
                    if len(path) >= len(longest):
                        longest = [node.coord] + path
            visited.remove(node)
            return longest

        node = self.nodes[y][x]
        if not node:
            raise "Cannot start from that position"
        visited = set()
        # look in both directions of starting node
        return recur(node, node.neighbors[0] + node.neighbors[1]) 


grid = [
    [0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0],
    [1, 1, 1, 0, 0],
    [0, 0, 0, 0, 0],
    [1, 0, 0, 1, 0]
]

maze = Maze(grid)
path = maze.findpath(2, 0)
print(path)  # output: [(2, 0), (2, 2), (2, 1), (1, 1)]

path = maze.findpath(4, 3)
print(path)  # output: [(4, 3), (4, 0), (2, 0), (2, 2), (2, 1), (1, 1)]

请注意,此解决方案中的坐标是从零开始的,因此第一行的编号为0,...等。

看到它在repl.it上运行

答案 1 :(得分:0)

您可以将递归与生成器一起使用:

d = [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [1, 1, 1, 0, 0], [0, 0, 0, 0, 0], [1, 0, 0, 1, 0]]
_d = {1:lambda a, b:'f' if b[-1] > a[-1] else 'b', 0:lambda a, b:'u' if b[0] > a[0] else 'd'}
def paths(start, _dir, c = []):
   yield c
   _options = [(a, b) for a in range(len(d)) for b in range(len(d[0])) if (a, b) not in c and d[a][b]]
   if _options:
      for a, b in _options:
         if a == start[0] or b == start[-1]:
            r = _d[a == start[0]](start, (a, b))
            if _dir is None or r != _dir:
               yield from paths((a, b), r, c+[(a, b)])


print(max(list(paths((4, 3), None, [(4, 3)])), key=len))

输出:

[(4, 3), (4, 0), (2, 0), (2, 2), (2, 1), (1, 1)]