OutputError:在给定约束的矩阵中查找最长路径

时间:2017-04-13 00:04:23

标签: python python-3.x recursion dynamic-programming depth-first-search

我的矩阵是:

4 8 7 3 
2 5 9 3 
6 3 2 5 
4 4 1 6

问题(滑雪):

每个数字代表该山区的海拔

从网格中的每个区域(即框),您可以去西 - 但前提是您要进入的区域的高度 小于您所在的区域。

即。你只能滑雪下坡。

您可以从地图上的任意位置开始,并且您正在寻找一个起点,其中可能的最长路径以您访问的方框数量来衡量。

如果有多条相同长度的路径,您想要使用最陡垂直下降的路径,即起始高程与结束高程之间的最大差异。

我的解决方案:

def findSkiPath():
    mySolution = [0] * 3
    mySolution[0] = 0 # Distance
    mySolution[1] = 0 # Drop
    cellIndexRow = 0
    cellIndexCol = 0
    myPath = []
    myMatrix = [[4, 5, 8, 7],[1, 1, 5, 9], [0, 7, 5, 5], [7, 4, 2, 9]]
    countRows = len(myMatrix)
    countCols = len(myMatrix[0])
    for i in range(0, countRows - 1):
        for j in range(0, countCols - 1):
            myValue = myMatrix[i][j]
            myPath.append(myValue)

            #check east
            cellIndexRow = i
            cellIndexCol = j + 1
            checkAdjacentCells(cellIndexRow, cellIndexCol, myValue, myMatrix , mySolution , myPath )

            #check west
            cellIndexRow = i
            cellIndexCol = j - 1
            checkAdjacentCells(cellIndexRow, cellIndexCol, myValue, myMatrix , mySolution , myPath )

            #check north
            cellIndexRow = i - 1
            cellIndexCol = j
            checkAdjacentCells(cellIndexRow, cellIndexCol, myValue, myMatrix , mySolution , myPath )            

            #check south
            cellIndexRow = i + 1
            cellIndexCol = j
            checkAdjacentCells(cellIndexRow, cellIndexCol, myValue, myMatrix , mySolution , myPath )
    print (mySolution)

def checkAdjacentCells(cellIndexRow, cellIndexCol, myValue, myMatrix , mySolution , myPath ):

    #The base case - If we go beyond the limits of the matrix
    if (cellIndexRow < 0 or cellIndexRow > (len(myMatrix) - 1) or cellIndexCol < 0 or cellIndexCol > (len(myMatrix[0]) - 1)):
        evaluateSolution(mySolution , myPath )
        return

    #check if the next cell has a lower value than the current cell
    tmpValue = myMatrix[cellIndexRow][cellIndexCol]
    if tmpValue < myValue:
        newPath = myPath
        newPath.append(tmpValue)

        r = cellIndexRow
        c = cellIndexCol
        #check east
        cellIndexRow = r
        cellIndexCol = c + 1
        checkAdjacentCells(cellIndexRow, cellIndexCol, tmpValue, myMatrix , mySolution , newPath )

        #check west
        cellIndexRow = r
        cellIndexCol = c - 1
        checkAdjacentCells(cellIndexRow, cellIndexCol, tmpValue, myMatrix , mySolution , newPath )

        #check north
        cellIndexRow = r - 1
        cellIndexCol = c
        checkAdjacentCells(cellIndexRow, cellIndexCol, tmpValue, myMatrix , mySolution , newPath )          

        #check south
        cellIndexRow = r + 1
        cellIndexCol = c
        checkAdjacentCells(cellIndexRow, cellIndexCol, tmpValue, myMatrix , mySolution , newPath )

    evaluateSolution(mySolution , myPath )

def evaluateSolution(mySolution , myPath ):

    myDistance = 1
    mySolutionDistance = int(mySolution[0])
    mySolutionDrop = int(mySolution[1])

    if myDistance < mySolutionDistance:
        return

    myDrop = myPath[0] - myPath[-1]

    if myDistance > mySolutionDistance or myDrop > mySolutionDrop:
        mySolution[0] = myDistance
        mySolution[1] = mySolutionDrop
        mySolution[2] = myPath

if __name__ == "__main__":

    findSkiPath()

的问题:

当前输出(距离,跌落,路径):

  

[1,0,[4,2,8,7,3,4,2,5,2,3,2,1,7,3,2,5,2,3,2,1,9] ,   3,5,2,3,2,1,7,3,2,1,6,3,2,1,2,4,3,2,1,2,1]]

预期产出:

  

[5,8,[9,5,3,2,1]]

在这个特定的地图上,最长的路径是长度= 5,掉落= 8(9-1 = 8),路径:9-5-3-2-1。

2 个答案:

答案 0 :(得分:2)

人们可以通过两种不同的方式接近所述挑战中的问题:

  1. 使用递归算法,如果满足给定的要求,则在迭代遍历矩阵元素时仅检查已经检查的有效路径

  2. 分两步完成:

    2.1。通过简单调用itertools模块可用函数permutations()

    获取所有可能路径上的迭代器

    2.2。从生成的路径中挑选出符合要求的路径

  3. 第二种方法的代码更易于编写和理解,但是对于4x4大小的矩阵而言已经存在大量可能的路径,因此实际上不可能为更大的矩阵大小运行它。

    第一种方法的代码可以处理更大尺寸的矩阵,但缺点是在其他约束的情况下更难理解它如何调整它。

    这里提出的问题是两年前提出的一个问题的 100%1:1重复 here on stackoverflow,标题为“矩阵路径中元素的最大数量”。无论如何,这里的解决方案再次回答了那个老问题:

    theMatrix = [
                    [ 4, 8, 7, 3],
                    [ 2, 5, 9, 3],
                    [ 6, 3, 2, 5],
                    [ 4, 4, 1, 6]
    ]
    
    def longest_path(matrix):
        def inner_longest_path(x, y):
            best, best_path = 0, []
            # for all possible neighbor cells...
            for dx, dy in ((+1, 0), (-1, 0), (0, +1), (0, -1)):
                # if cell is valid and strictly smaller...
                if (0 <= x + dx < len(matrix) and 0 <= y + dy < len(matrix[x]) 
                        and matrix[x+dx][y+dy] < matrix[x][y]):
                    n, path = inner_longest_path(x+dx, y+dy)  ### RECURSION
                    # check if the path starting at that cell is better
                    if n > best:
                        best, best_path = n, path
            return best + 1, [matrix[x][y]] + best_path
    
        return max(inner_longest_path(x, y) for x, row in enumerate(matrix) 
                                             for y, _ in enumerate(row))
    
    print( longest_path(theMatrix) )
    

    上面的代码打印:

    (5, [9, 5, 3, 2, 1])
    

    现在让我们来看看这里提供的非递归方法的代码:我自己:

    # myMatrix = [[4, 5, 8, 7],[1, 1, 5, 9], [0, 7, 5, 5], [7, 4, 2, 9]]
    #  4 5 8 7
    #  1 1 5 9 
    #  0 7 5 5
    #  7 4 2 9
    myMatrix = [[4, 5, 8],[1, 1, 5], [0, 7, 5]]
    #  4 5 8
    #  1 1 5
    #  0 7 5
    # myMatrix = [[4, 5],[1, 1]]
    #  4 5
    #  1 1
    
    def getAllValidSkiingPathsFrom(myMatrix): 
    # def getDictRepresentationOf(myMatrix):
        dctOfMatrix = {}
        for row in range(len(myMatrix)):
            for column in range(len(myMatrix[0])):
                currPoint = (column, row)
                dctOfMatrix[currPoint] = myMatrix[row][column]
    
        lstIndicesOfAllMatrixPoints = list(dctOfMatrix.keys())
        setAllPossiblePaths = set()
    
        from itertools import permutations
        for pathCandidate in permutations(lstIndicesOfAllMatrixPoints): 
            lstPossiblePath = []
            prevIndexTuple = pathCandidate[0]
            lstPossiblePath.append(prevIndexTuple)
            for currIndexTuple in pathCandidate[1:]:
                if abs(currIndexTuple[0]-prevIndexTuple[0]) + abs(currIndexTuple[1]-prevIndexTuple[1]) > 1:
                    break # current path indices not allowed in path (no diagonals or jumps)
                else:
                    if dctOfMatrix[currIndexTuple] >= dctOfMatrix[prevIndexTuple]: 
                        break # only "down" is allowed for "skiing" 
                    else:
                        lstPossiblePath.append(currIndexTuple)
                        prevIndexTuple = currIndexTuple
            if len(lstPossiblePath) > 1 and tuple(lstPossiblePath) not in setAllPossiblePaths: 
                setAllPossiblePaths.add(tuple(lstPossiblePath))
    
        return setAllPossiblePaths, dctOfMatrix
    #:def getAllValidSkiingPathsFrom
    
    setAllPossiblePaths, dctOfMatrix = getAllValidSkiingPathsFrom(myMatrix)
    
    for path in setAllPossiblePaths:
        for point in path:
            print(dctOfMatrix[point], end=',')
    

    这里是myMatrix的2x2和3x3版本的结果:

    #  4 5
    #  1 1
    4,1,
    5,1,
    5,4,
    5,4,1,
    
    #   4 5 8
    #   1 1 5
    #   0 7 5
    5,1,
    8,5,1,
    7,1,
    4,1,
    5,1,
    5,4,
    8,5,1,
    1,0,
    5,4,1,0,
    8,5,4,1,
    8,5,4,1,0,
    8,5,4,
    8,5,
    7,0,
    7,5,
    8,5,
    4,1,0,
    5,4,1,
    

    我希望代码是不言自明的,但如果不是这里粗略的想法:

    1. 构建一个表示矩阵的字典,其中键是(列,行)“坐标”的元组,而值是矩阵中的值。

    2. 建立矩阵内所有可能的完整路径列表(排列)

    3. 过滤所有可能的完整路径列表,根据需要仅提取有效路径(应用标准)。

    4. 我没有运行计算非常昂贵的4x4矩阵结果计算,因为它在我的盒子上肯定需要几分钟。

      为了完整起见,我想提一下,还有另一个问题HERE on stackoverflow,它是这个问题的一个变体(它有一些其他有效路径的规则,并要求算法能够处理不规则的问题矩阵)。

答案 1 :(得分:1)

这个问题的主要问题是你不做任何回溯。您正在适当地遍历矩阵,但是您没有做任何事情来维护特定路径的概念。相反,对于您访问的每个方格,您只需将所有合法移动附加到单个列表中。

相反,请考虑将当前路径保持为局部变量。对于来自给定方格的每个合法移动,您进行单独的递归调用以查找更多移动。其中每一项都会在本地路径的末尾添加合法移动。

当您从每次通话中返回时,将找到的最佳路径与您保存的路径进行比较(目前为止最好);保持这两者的优势,并进入下一个法律行动。当您考虑了四个方向中的每个方向时,请将最有名的路径返回到调用实例。

在线和本网站上有很多回溯的例子;找出一些你觉得可以理解的东西。您可以查看硬币组合递归(找到一组添加到一定数量的硬币) - 它们不是相同的算法,但上面的回溯想法会更清楚地显示出来。

这会让你走向解决方案吗?