【问题标题】:How can I speed up/fix this shortest path grid traversal?如何加快/修复这个最短路径网格遍历?
【发布时间】:2021-12-05 03:15:12
【问题描述】:

我正在编写一个程序来遍历从左下角到右上角的网格,其中有效的移动是向右移动 1,向上移动 1,或者向上或向右跳跃指定的数量跳转数组。输入是二维数组格式的网格、网格的维度:X 和 Y,以及必须按顺序完成跳跃的跳跃数组。输出是最短路径,其中路径的长度是所有触摸的数字的总和,包括左下角和右上角。
示例输入:

Grid: 
    [9 9 1]
    [9 9 1]    
    [3 9 1]

Jumps: [1 1] X:3, Y:3

输出将是 5,因为我们从 (0,0) 开始,即 3,然后使用第一个 1 块跳转到 (2, 0),即 1,然后第二个块跳转到 (2, 2) 是 1,所以 3+1+1 = 5。如果 jumps 数组只有 [1],那么输出将是 6,因为我们必须从 (2,0) 移动到 (2,1) 到 (2 ,2)。

这是我的第一个解决方案,它似乎适用于较小的输入,但只能在较大的输入上永远运行:

def get(grid, x, y, endX, endY):
    if (x > endX or y > endY):
        return False
    return grid[y][x]

def fly(x, y, grid, jumps, endX, endY):
    if x > endX or y > endY:
        return float('inf')
    if (x == endX and y == endY):
        return get(grid, endX, endY, endX, endY)
    flyup = fly(x, y+1, grid, jumps, endX, endY)
    flyright = fly(x+1, y, grid, jumps, endX, endY)
    if (len(jumps) > 0):
        jumpup = fly(x, y+jumps[0]+1, grid, jumps[1:], endX, endY)
        jumpright = fly(x+jumps[0]+1, y, grid, jumps[1:], endX, endY)
        temp = min(flyup, flyright, jumpup, jumpright)
        return get(grid, x, y, endX, endY) + temp
    else:
        temp = min(flyup, flyright)
        return get(grid, x, y, endX, endY) + temp

fly(0, 0, grid, jumps, X-1, Y-1)

这是我使用 DP 编写的另一个解决方案(或者我对 DP 的理解,因为我刚学会它),但这个似乎只适用于某些输入,我无法识别模式。

def fly2(x, y, grid, jumps):
    dp = [[0 for i in range(len(grid[0]))] for j in range(len(grid))]
    for row in range(len(grid)):
        for col in range(len(grid[0])):
            if row == 0 and col == 0:
                dp[row][col] += get2(grid, col, row)
            else:
                flyup = float('inf') if row==0 else dp[row-1][col]
                flyright = float('inf') if col==0 else dp[row][col-1]
                jumpup =  float('inf') if row < jumps[0]+1 else dp[row-jumps[0]-1][col]
                jumpright =  float('inf') if col < jumps[0]+1 else dp[row][col-jumps[0]-1]
                shortest = min(flyup, flyright, jumpup, jumpright)
                if min == jumpup or min == jumpright:
                    jumps = jumps[1:]
                dp[row][col] += get2(grid, col, row) + shortest
    return dp[len(grid)-1][len(grid[0])-1]

我需要一些帮助来加快第一个或找出第二个有什么问题,或者获得一些关于如何有效编写此代码的其他想法。谢谢

【问题讨论】:

  • DP表需要另外一个维度,就是使用的跳转次数。

标签: python algorithm dynamic-programming


【解决方案1】:

在我看来,dp 应该为当前的跳转索引有另一个维度。一般来说,

dp[y][x][j] = Grid[y][x] + min(
  dp[y + 1][x][j],
  dp[y][x - 1][j],
  dp[y + jump[j-1] + 1][x][j-1],
  dp[y][x - jump[j-1] - 1][j-1]
)

其中j 是跳转数组中的当前索引。

(我认为问题描述在示例中使用了(x,y)坐标表示法。我使用[y][x]作为[row][column],常见用于编程二维数组访问。)

【讨论】:

  • 跳跃维度的长度是多少?我对如何构建它有点困惑
  • @anoncomp 跳转维度对应于我们当前在跳转数组中的索引。它的长度会比后者大 1,说明我们用尽了所有的跳跃。
  • 等一下,抱歉,那我们为什么要向后遍历跳跃呢? (比如做 j-1?因为从技术上讲,你不需要使用每一个跳跃)
  • @anoncomp 问题描述指出“必须按顺序完成跳跃”。跳跃指数的维度让我们可以选择评估特定单元格在所有可能状态下的最佳值(通常)。
  • @anoncomp 的意思是,如果我们选择在到达当前状态之前使用跳转,那么在之前的状态中我们必须在索引j-1
【解决方案2】:

以下实现使用网格和剩余的跳转作为状态变量:

import numpy as np

def shortest(grid, jumps):
    rows, cols = grid.shape
    if (rows, cols) == (1, 1): # if grid is just a single number
        return float('inf') if jumps else grid[0, 0]
    candidates = [] # store results from deeper calls
    if rows > 1:
        candidates.append(shortest(grid[:-1, :], jumps)) # up by one
        if jumps and rows > jumps[0] + 1: # jump to the above if possible
            candidates.append(shortest(grid[:-(jumps[0] + 1), :], jumps[1:]))
    if cols > 1:
        candidates.append(shortest(grid[:, 1:], jumps)) # right by one
        if jumps and cols > jumps[0] + 1: # jump to the right if possible
            candidates.append(shortest(grid[:, (jumps[0] + 1):], jumps[1:]))
    return grid[-1, 0] + min(candidates)

grid = np.array([[9, 9, 1], [9, 9, 1], [3, 9, 1]])
jumps = [1, 1]
print(shortest(grid, jumps)) # 5

np.array 的使用只是为了简化切片。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多