1210. 穿过迷宫的最少移动次数

124 阅读3分钟

题目:
你还记得那条风靡全球的贪吃蛇吗?

我们在一个 n*n 的网格上构建了新的迷宫地图,蛇的长度为 2,也就是说它会占去两个单元格。蛇会从左上角((0, 0) 和 (0, 1))开始移动。我们用 0 表示空单元格,用 1 表示障碍物。蛇需要移动到迷宫的右下角((n-1, n-2) 和 (n-1, n-1))。

每次移动,蛇可以这样走:

  • 如果没有障碍,则向右移动一个单元格。并仍然保持身体的水平/竖直状态。
  • 如果没有障碍,则向下移动一个单元格。并仍然保持身体的水平/竖直状态。
  • 如果它处于水平状态并且其下面的两个单元都是空的,就顺时针旋转 90 度。蛇从((r, c)(r, c+1))移动到 ((r, c)(r+1, c))。

算法:
本身不算难,理清楚6种可能的移动状态,用status表示横竖位置比用另外一个坐标表示蛇首要简洁很多。
方法一:DFS+剪枝

func minimumMoves(grid [][]int) int {
    n := len(grid)
    ans := math.MaxInt32
    // 走到某个位置时及对应的步数
    cache := make(map[int]int)
    var dfs func(step int, x, y, status int, rotate bool)
    dfs = func(step int, x, y, status int, rotate bool) {

        if x == n - 1 && y == n - 2 && status == 0 {
            ans = min(ans, step)
            return 
        }
        // 不剪枝会超时,不能说访问过这个位置,就跳过。而是如果到当前位置有更少的step就跳过。
        if 0 < cache[getKey(x, y, status)] && cache[getKey(x, y, status)] <= step {
            return
        }
        cache[getKey(x, y, status)] = step
        // 水平右移
        if status == 0 && y + 2 < n && grid[x][y + 2] == 0 { 
            dfs(step + 1, x, y + 1, status, false)
        }
        // 水平下移
        if status == 0 && x + 1 < n && grid[x + 1][y] == 0 && grid[x + 1][y + 1] == 0 {
            dfs(step + 1, x + 1, y, status, false)
        }
        // 水平顺时针旋转
        if !rotate && status == 0 && x + 1 < n && grid[x + 1][y] == 0 && grid[x + 1][y + 1] == 0 {
            dfs(step + 1, x, y,  1, true)
        }
        // 竖直右移
        if status == 1 && y + 1 < n && grid[x][y + 1] == 0 && grid[x + 1][y + 1] == 0 { 
            dfs(step + 1, x, y + 1, status, false)
        }
        // 竖直下移
        if status == 1 && x + 2 < n &&  grid[x + 2][y] == 0 {
            dfs(step + 1, x + 1, y, status, false)
        }

        // 竖直逆时针旋转
        if !rotate && status == 1 && y + 1 < n && grid[x][y + 1] == 0 && grid[x + 1][y + 1] == 0 {
            dfs(step + 1, x, y, 0, true)
        }
        
    }
    dfs(0, 0, 0, 0, false)
    if ans == math.MaxInt32 {
        return -1
    }
    return ans
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func getKey(x, y, status int) int {
    return status * 10000 + x * 100 + y
}

方法二:BFS
BFS每次增加一个step,保证最小达到(n-1,n-2,0)时经过的步数就是结果。

func minimumMoves(grid [][]int) int {
    n := len(grid)

    visited := make([][][2]int, n)
    for i := range visited {
        visited[i] = make([][2]int, n)
    }
    queue := []tuple{tuple{0, 0, 0}}
    for step := 0; len(queue) != 0 ; step ++ {
        // 如果当前位置已经到达右下角,返回step
        length := len(queue)
        for i := 0; i < length; i ++ {
            x, y, status := queue[i].i, queue[i].j, queue[i].status
            if x == n - 1 && y == n - 2 && status == 0 {
                return step
            }
            if visited[x][y][status] == 1{
                continue
            }
             visited[x][y][status] = 1

            // 水平右移
            if status == 0 && y + 2 < n && grid[x][y + 2] == 0 { 
                queue = append(queue, tuple{x, y + 1, status})
            }
            // 水平下移
            if status == 0 && x + 1 < n && grid[x + 1][y] == 0 && grid[x + 1][y + 1] == 0 {
                queue = append(queue, tuple{x + 1, y, status})
            }
            // 水平顺时针旋转
            if status == 0 && x + 1 < n && grid[x + 1][y] == 0 && grid[x + 1][y + 1] == 0 {
                queue = append(queue, tuple{x, y, 1})
            }
            // 竖直右移
            if status == 1 && y + 1 < n && grid[x][y + 1] == 0 && grid[x + 1][y + 1] == 0 { 
                queue = append(queue, tuple{x, y + 1, status})
            }
            // 竖直下移
            if status == 1 && x + 2 < n &&  grid[x + 2][y] == 0 {
               queue = append(queue, tuple{x + 1, y, status})
            }

            // 竖直逆时针旋转
            if status == 1 && y + 1 < n && grid[x][y + 1] == 0 && grid[x + 1][y + 1] == 0 {
                queue = append(queue, tuple{x, y, 0})
            }
        }
        // 走六种方向,如果能走则加入queue
        queue = queue[length:]
    }
    return -1
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

type tuple struct {
    i int
    j int
    status int
}