【问题标题】:Finding longest path using recursion with a set of rules使用带有一组规则的递归查找最长路径
【发布时间】:2019-05-13 06:54:38
【问题描述】:

对于一些背景知识,我根本不是计算机科学家或程序员(我在大学学习物理时学习了一些 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 有一点经验,但我从未尝试过递归(我很确定这是解决这个问题的方法),而且我似乎无法在网上找到任何符合我水平的帮助。

【问题讨论】:

  • 为了确保正确理解问题:有效的移动应该是从一个1 到另一个1,但也可以是多个0s,对吗? 'on-diagonal',你的意思是说你可以跳到对角线上的一个值,然后在对角线的另一边沿相同的方向继续前进吗?如果您提供一个较小的示例矩阵和解决方案(手动计算),这可能会有所帮助。
  • 感谢您的反馈。我做了一些编辑,希望能澄清一点。 Grismar,是的,它允许跳过 0,是的,当你跳到对角线时,你可以朝同一个方向跳(通常是禁止的)。
  • 在您的问题陈述中,您说“从给定的“1”位置开始。这是否意味着将起点作为例程的输入给出,还是意味着无论起点位置如何,您都想找到最长的路径?

标签: python recursion path-finding


【解决方案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上运行

【讨论】:

    【解决方案2】:

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

    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)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-02
      • 2016-01-20
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 2017-09-24
      相关资源
      最近更新 更多