【问题标题】:Matrix traversal not doing optimal path矩阵遍历没有做最优路径
【发布时间】:2020-03-23 11:47:14
【问题描述】:

我遇到了以下我正在尝试解决的问题:

在 N x N grid 中,代表一片樱桃,每个单元格都是三个可能的整数之一。

  • 0表示单元格为空,可以通过;
  • 1 表示该单元格包含一个樱桃,您可以捡起并通过;
  • -1 表示牢房中有一根刺挡住了你的去路。

您的任务是按照以下规则收集尽可能多的樱桃:

  • 从位置 (0, 0) 开始,通过向右或向下移动穿过有效路径单元(具有 > 值 0 或 1 的单元)到达 (N-1, N-1);
  • 到达 (N-1, N-1) 后,通过有效路径单元向左或向上移动返回 (0, 0);
  • 通过包含樱桃的路径单元格时,您将其捡起,该单元格变为空单元格 (0);
  • 如果 (0, 0) 和 (N-1, N-1) 之间没有有效路径,则无法采集樱桃。
Example 1:

Input: grid =
[[0, 1, -1],
 [1, 0, -1],
 [1, 1,  1]]

Output: 5

Explanation: 
The player started at (0, 0) and went down, down, right right to reach (2, 2).
4 cherries were picked up during this single trip, and the matrix becomes [[0,1,-1],[0,0,-1],[0,0,0]].
Then, the player went left, up, up, left to return home, picking up one more cherry.
The total number of cherries picked up is 5, and this is the maximum possible.

注意:

  • gridN by N 的二维数组,带有 1 <= N <= 50
  • 每个grid[i][j] 都是{-1, 0, 1} 集合中的一个整数。
  • 保证grid[0][0]和grid[N-1][N-1]不是-1。

所以我需要编写一个函数cherryPickup,它接受一个网格并返回最高分。

我的第一个次优尝试(用 Go 编写)如下,据说它会尝试走每条可能的路径,在往返路径完成时将分数存储在切片中,然后返回切片中存在的最大分数:

func cherryPickup(grid [][]int) int {

    values := []int{}
    pVals := &values
    finalPoints := 0

    // Begin top-down path
    traverseAndCollectTopDown(grid, 0, 0, 0, pVals)

    // Find max value in slice
    for i, pathPoints := range values {
        if i == 0 || pathPoints > finalPoints {
            finalPoints = pathPoints
        }
    }

    return finalPoints
}

func isTraversable(grid [][]int, x, y int) bool {
    return (grid[x][y] != -1)
}

func isOnBounds(grid [][]int, x, y int) bool {
    return (x < len(grid) && y < len(grid[0]) && x >= 0 && y >= 0)
}

func traverseAndCollectTopDown(grid [][]int, x, y, points int, vals *[]int) {

    // Collect point before continuing
    if grid[x][y] == 1 {
        grid[x][y] = 0
        points++
    }

    // If reached bottom, begin bottom-up path
    if (x == len(grid)-1) && (y == len(grid[0])-1) {
        traverseAndCollectBottomUp(grid, x, y, points, vals)
        return
    }

    // Go Down
    if isOnBounds(grid, x+1, y) && isTraversable(grid, x+1, y) {
        traverseAndCollectTopDown(grid, x+1, y, points, vals)
    }
    // Go Right
    if isOnBounds(grid, x, y+1) && isTraversable(grid, x, y+1) {
        traverseAndCollectTopDown(grid, x, y+1, points, vals)
    }

}

func traverseAndCollectBottomUp(grid [][]int, x, y, points int, vals *[]int) {

    if grid[x][y] == 1 {
        grid[x][y] = 0
        points++
    }

    if x == 0 && y == 0 {
        *vals = append(*vals, points)
        return
    }

    // Go Up
    if isOnBounds(grid, x-1, y) && isTraversable(grid, x-1, y) {
        traverseAndCollectBottomUp(grid, x-1, y, points, vals)
    }
    // Go Left
    if isOnBounds(grid, x, y-1) && isTraversable(grid, x, y-1) {
        traverseAndCollectBottomUp(grid, x, y-1, points, vals)
    }
}

目前它通过了一堆测试,但是这个失败了,我不知道为什么。

输入:[[1,1,1,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,1],[1,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,1,1,1]]

输出:10

预计:15

我知道这个网格必须获得 15 分,但是,为什么我的代码无法通过获胜路径而只获得 10 分?另外,您是否推荐任何终端实用程序、程序或策略来帮助更好地可视化每次运行时发生的情况?

提前致谢!

【问题讨论】:

  • 您介意分享您的测试套件吗?
  • 恐怕我不能@mh-cbon,我正在通过一个网站(LeetCode)进行练习,他们只输出导致整个套件失败的特定测试用例:(

标签: arrays algorithm go recursion matrix


【解决方案1】:

您用于traverseAndCollectTopDown / traverseAndCollectBottomUp 的参数是grid [][]int。您正在修改它,然后将其直接传递给其他函数(递归)。在 go 中,切片实际上是passed by reference,这意味着当您的一个例程编辑切片时,这也会影响所有其他例程持有的切片(因此,一旦一条路径找到“1”,它就会被删除,另一条路径将继续通过同一个单元格将在那里找到一个“0”)。

要解决此问题,请在进行递归调用之前获取网格副本,例如在修改网格之前调用grid = copyGrid(grid) in traverseAndCollectTopDown / traverseAndCollectBottomUp

func copyGrid(in [][]int) [][]int {
    duplicate := make([][]int, len(in))
    for i := range in {
        duplicate[i] = make([]int, len(in[i]))
        copy(duplicate[i], in[i])
    }
    return duplicate 
}

【讨论】:

  • 啊,我明白了!那将使我对得分片的指针的使用毫无用处,对吗?我尝试删除它,但它输出 0 分......无论如何,我把指针留在那里,它似乎已经通过了之前的测试。现在我必须解决测试套件中大型矩阵的时间限制...?非常感谢!
  • 不完全(重新指向切片)。如果您附加到一个切片并且它需要增长(查找上限),那么您将获得一个全新的切片(这意味着任何更改都不会影响调用者的副本)。在您的代码中,您确实在 vals 上调用了 append,因此应该将其作为指针传递。有关这方面的更多信息,请参阅blog.golang.org/slices
  • 是的,我想是因为这个。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-16
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 2016-02-12
相关资源
最近更新 更多