使用向右或向下跳跃或单位步长在网格中的最小成本路径

Posted

技术标签:

【中文标题】使用向右或向下跳跃或单位步长在网格中的最小成本路径【英文标题】:Min cost path in grid using jumps or unit steps right or down 【发布时间】:2021-12-05 05:22:48 【问题描述】:

假设您有一个网格,其中每个节点都包含一个权重,该权重代表取该正方形的成本。

您从二维数组左上角的 (0,0) 开始,想要到达 (X-1, Y-1),其中 X 是右下角的列数和 Y 行数。您可以向右走 1 格,也可以一次向下走 1 格。您还会得到一个“跳跃”的 int 数组,其中每个值 $d_i$ 表示您可以在第 i 次跳跃时向右或向下跳过的方格数。 jumps 数组指定了您必须采取的跳跃顺序,这意味着,例如,如果您没有使用过一个跳跃,则不能使用 jump[2] 。你只能采取 len(jump) 的跳跃次数。

我们正在尝试通过动态规划来解决这个问题。这是我迄今为止所拥有的:

def helper(grid, jumps):
    dCount = 0
    dp = [[[0 for k in range(len(jumps)+1)] for j in  range(len(grid[0]))] for i 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][dCount] += grid[row][col]
            jumpRight = float('infinity')
            jumpUp = float('infinity')
            goUp = float('infinity')
            goRight = float('infinity')
            if(row > 0):
                goUp = dp[row-1][col][dCount]
            if(col > 0):
                goRight = dp[row][col-1][dCount]
            if (dCount < len(jumps)):
                jumpRight = dp[row-jumps[dCount]][col][dCount] 
                jumpUp = dp[row][col-jumps[dCount]][dCount]
            res = grid[row][col] + min(goRight, goUp, jumpRight, jumpUp)
            if(res == jumpRight or res==jumpUp): 
                dCount+=1
            dp[row][col][dCount] = res
    return dp[len(grid)-1][len(grid[0])-1]

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

jumps = [1, 1]
print(helper(grid,jumps)) #should be 16

但是,代码似乎不起作用。

上面只是打印出[0 0 15],我认为应该是[0 0 16]

【问题讨论】:

它有很大帮助,但我得到的答案是 0,尽管从概念上讲它是有道理的:( @גלעדברקן @גלעדברקן 是的,我在 res = grid[row][col] + min(goRight, goUp, jumpRight, jumpUp) 添加了值,我的基本情况是如果 row=col=0 (您只需添加 grid[row][col]) @גלעדברקן 你知道为什么它返回 15 吗? :0 【参考方案1】:

以下是一个实现:

def shortest(grid, jumps, x=0, y=0, jumped=0):
    # grid: whole grid
    # jumps: jumps to be exhuasted
    # x, y: current position
    # jumped: number of jumps that have been used
    # output: a tuple (cost, path)
    #         cost: minimum cost from this position to the final position
    #         path: a list of tuples, each element the position along the minimum-cost path
    rows = len(grid) - x; cols = len(grid[0]) - y # remaining num of rows and cols
    if (rows, cols) == (1, 1): # if arrived at the southeast corner
        return (float('inf'), [(x, y)]) if jumps[jumped:] else (grid[x][y], [(x, y)]) # return inf if jumps are not exhausted
    candidates = [] # store results from deeper calls
    if rows > 1: # if possible to move down
        candidates.append(shortest(grid, jumps, x+1, y, jumped)) # down by one
        if jumped < len(jumps) and rows > jumps[jumped] + 1: # if possible to jump
            candidates.append(shortest(grid, jumps, x+jumps[jumped]+1, y, jumped+1)) # jump down
    if cols > 1: # if possible to move right
        candidates.append(shortest(grid, jumps, x, y+1, jumped)) # right by one
        if jumped < len(jumps) and cols > jumps[jumped] + 1: # if possible to jump
            candidates.append(shortest(grid, jumps, x, y+jumps[jumped]+1, jumped+1)) # jump right
    temp = min(candidates, key=lambda x: x[0]) # recall: temp[0]: min_cost, temp[1]: min_path
    return (temp[0] + grid[x][y], [(x, y), *temp[1]])

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

jumps = [1, 1]
print(shortest(grid, jumps)) # (16, [(0, 0), (0, 1), (0, 2), (0, 4), (2, 4), (2, 5), (3, 5)])

状态变量为xyjumpsxy 记住当前位置在给定网格中的位置。 jumps 记得到目前为止已经使用了多少次跳转。

递归的结束状态发生在位置处于最终目的地时,即[-1, -1]。如果跳跃没有用尽,那么它是失败的;所以返回无穷大作为成本。否则,返回该点的值作为成本。

其实返回的是一个元组;第一个元素是成本,第二个元素是当前位置。第二个元素用于最终获得成本最小化路径。

在其他状态下,递归是以自然方式定义的;该函数递归到(最多)四种可能的运动:上一、右一、上跳、右跳。它比较产生的成本,并选择最小成本的方向。

注意:这里使用generalized unpack,在 python 3.5 中引入。但这不是必需的。

【讨论】:

【参考方案2】:

这是一个自下而上方法的示例,它似乎返回了此示例和a similar question 中的正确结果。不过,我不确定代码是否万无一失。

Python:

def f(grid, jumps):
  m, n = len(grid), len(grid[0])
  dp = [None] * m

  for i in range(m):
    dp[i] = [[float('inf')] * (len(jumps) + 1) for j in range(n)]

  dp[0][0][0] = grid[0][0]

  for y in range(m):
    for x in range(n):
      for j in range(len(jumps) + 1):
        if (y, x, j) == (0, 0, 0):
          continue

        dp[y][x][j] = grid[y][x] + min(
          dp[y - 1][x][j] if y > 0 else float('inf'),
          dp[y][x - 1][j] if x > 0 else float('inf'),
          dp[y - jumps[j-1] - 1][x][j-1] if (j > 0 and y - jumps[j-1] - 1 >= 0) else float('inf'),
          dp[y][x - jumps[j-1] - 1][j-1] if (j > 0 and x - jumps[j-1] - 1 >= 0) else float('inf')
        )
  
  return min(dp[m-1][n-1])


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

jumps = [1, 1]

print(f(grid, jumps)) # 16


grid = [
  [3, 9, 1],
  [9, 9, 1],
  [9, 9, 1]
]

jumps = [1, 1]

print(f(grid, jumps)) # 5

【讨论】:

以上是关于使用向右或向下跳跃或单位步长在网格中的最小成本路径的主要内容,如果未能解决你的问题,请参考以下文章

跳跃版图

矩阵从左上到右下导航,只能向右或向下移动?

通过矩阵的最小成本

以最少的运行次数遍历网格(图形)的每个边缘

leetcode 62. 不同路径

LeetCode不同路径