Hard

题目描述

在一个 n×n 的网格中,有一条蛇占据两个格子,从左上角的 (0, 0)(0, 1) 开始移动。网格中用 0 表示空格子,用 1 表示被阻塞的格子。蛇想要到达右下角的 (n-1, n-2)(n-1, n-1)

在一次移动中,蛇可以:

  • 向右移动一格:如果那里没有被阻塞的格子。这种移动保持蛇的水平/垂直位置不变。
  • 向下移动一格:如果那里没有被阻塞的格子。这种移动保持蛇的水平/垂直位置不变。
  • 顺时针旋转:如果蛇处于水平位置且其下方的两个格子都是空的。在这种情况下,蛇从 (r, c)(r, c+1) 移动到 (r, c)(r+1, c)
  • 逆时针旋转:如果蛇处于垂直位置且其右侧的两个格子都是空的。在这种情况下,蛇从 (r, c)(r+1, c) 移动到 (r, c)(r, c+1)

返回到达目标位置的最少移动次数。如果无法到达目标,返回 -1

示例 1:

输入:grid = [[0,0,0,0,0,1],
               [1,1,0,0,1,0],
               [0,0,0,0,1,1],
               [0,0,1,0,1,0],
               [0,1,1,0,0,0],
               [0,1,1,0,0,0]]
输出:11

示例 2:

输入:grid = [[0,0,1,1,1,1],
               [0,0,0,0,1,1],
               [1,1,0,0,0,1],
               [1,1,1,0,0,1],
               [1,1,1,0,0,1],
               [1,1,1,0,0,0]]
输出:9

提示:

  • 2 <= n <= 100
  • 0 <= grid[i][j] <= 1
  • 保证蛇从空格子开始

解题思路

这是一个典型的最短路径问题,可以使用广度优先搜索(BFS)来解决。

思路分析

蛇的状态可以用三个参数描述:

  1. 蛇头位置 (row, col)
  2. 蛇尾位置(通过方向确定)
  3. 蛇的方向(水平 or 垂直)

我们可以用一个三元组 (row, col, direction) 表示蛇的状态,其中 direction = 0 表示水平(蛇尾在右边),direction = 1 表示垂直(蛇尾在下方)。

BFS 搜索策略

从初始状态 (0, 0, 0) 开始,每次尝试四种操作:

  1. 向右移动(r, c)(r, c+1)
  2. 向下移动(r, c)(r+1, c)
  3. 顺时针旋转:水平 → 垂直
  4. 逆时针旋转:垂直 → 水平

对于每种操作,需要检查:

  • 目标位置是否在网格范围内
  • 蛇身占据的所有格子是否为空(值为0)
  • 旋转时需要额外检查旋转所需的空间

状态验证

  • 向右/向下移动:检查新的蛇头和蛇尾位置是否都为空
  • 顺时针旋转(水平→垂直):检查旋转后的蛇尾位置和旋转过程中的对角位置是否为空
  • 逆时针旋转(垂直→水平):检查旋转后的蛇尾位置和旋转过程中的对角位置是否为空

使用 visited 集合避免重复访问相同状态,当到达目标状态 (n-1, n-2, 0) 时返回步数。

代码实现

class Solution {
public:
    int minimumMoves(vector<vector<int>>& grid) {
        int n = grid.size();
        queue<tuple<int, int, int, int>> q; // row, col, direction, steps
        set<tuple<int, int, int>> visited;
        
        q.push({0, 0, 0, 0}); // start at (0,0) horizontal, 0 steps
        visited.insert({0, 0, 0});
        
        while (!q.empty()) {
            auto [r, c, dir, steps] = q.front();
            q.pop();
            
            // Check if reached target
            if (r == n-1 && c == n-2 && dir == 0) {
                return steps;
            }
            
            // Try all possible moves
            vector<tuple<int, int, int>> nextStates;
            
            if (dir == 0) { // horizontal
                // Move right
                if (c + 2 < n && grid[r][c+2] == 0) {
                    nextStates.push_back({r, c+1, 0});
                }
                // Move down
                if (r + 1 < n && grid[r+1][c] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.push_back({r+1, c, 0});
                }
                // Rotate clockwise (horizontal to vertical)
                if (r + 1 < n && grid[r+1][c] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.push_back({r, c, 1});
                }
            } else { // vertical
                // Move right
                if (c + 1 < n && grid[r][c+1] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.push_back({r, c+1, 1});
                }
                // Move down
                if (r + 2 < n && grid[r+2][c] == 0) {
                    nextStates.push_back({r+1, c, 1});
                }
                // Rotate counterclockwise (vertical to horizontal)
                if (c + 1 < n && grid[r][c+1] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.push_back({r, c, 0});
                }
            }
            
            for (auto [nr, nc, ndir] : nextStates) {
                if (visited.find({nr, nc, ndir}) == visited.end()) {
                    visited.insert({nr, nc, ndir});
                    q.push({nr, nc, ndir, steps + 1});
                }
            }
        }
        
        return -1;
    }
};
class Solution:
    def minimumMoves(self, grid: List[List[int]]) -> int:
        n = len(grid)
        queue = deque([(0, 0, 0, 0)])  # row, col, direction, steps
        visited = set([(0, 0, 0)])
        
        while queue:
            r, c, direction, steps = queue.popleft()
            
            # Check if reached target
            if r == n-1 and c == n-2 and direction == 0:
                return steps
            
            next_states = []
            
            if direction == 0:  # horizontal
                # Move right
                if c + 2 < n and grid[r][c+2] == 0:
                    next_states.append((r, c+1, 0))
                # Move down
                if r + 1 < n and grid[r+1][c] == 0 and grid[r+1][c+1] == 0:
                    next_states.append((r+1, c, 0))
                # Rotate clockwise (horizontal to vertical)
                if r + 1 < n and grid[r+1][c] == 0 and grid[r+1][c+1] == 0:
                    next_states.append((r, c, 1))
            else:  # vertical
                # Move right
                if c + 1 < n and grid[r][c+1] == 0 and grid[r+1][c+1] == 0:
                    next_states.append((r, c+1, 1))
                # Move down
                if r + 2 < n and grid[r+2][c] == 0:
                    next_states.append((r+1, c, 1))
                # Rotate counterclockwise (vertical to horizontal)
                if c + 1 < n and grid[r][c+1] == 0 and grid[r+1][c+1] == 0:
                    next_states.append((r, c, 0))
            
            for nr, nc, ndir in next_states:
                if (nr, nc, ndir) not in visited:
                    visited.add((nr, nc, ndir))
                    queue.append((nr, nc, ndir, steps + 1))
        
        return -1
public class Solution {
    public int MinimumMoves(int[][] grid) {
        int n = grid.Length;
        var queue = new Queue<(int r, int c, int dir, int steps)>();
        var visited = new HashSet<(int, int, int)>();
        
        queue.Enqueue((0, 0, 0, 0)); // start at (0,0) horizontal, 0 steps
        visited.Add((0, 0, 0));
        
        while (queue.Count > 0) {
            var (r, c, dir, steps) = queue.Dequeue();
            
            // Check if reached target
            if (r == n-1 && c == n-2 && dir == 0) {
                return steps;
            }
            
            var nextStates = new List<(int, int, int)>();
            
            if (dir == 0) { // horizontal
                // Move right
                if (c + 2 < n && grid[r][c+2] == 0) {
                    nextStates.Add((r, c+1, 0));
                }
                // Move down
                if (r + 1 < n && grid[r+1][c] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.Add((r+1, c, 0));
                }
                // Rotate clockwise (horizontal to vertical)
                if (r + 1 < n && grid[r+1][c] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.Add((r, c, 1));
                }
            } else { // vertical
                // Move right
                if (c + 1 < n && grid[r][c+1] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.Add((r, c+1, 1));
                }
                // Move down
                if (r + 2 < n && grid[r+2][c] == 0) {
                    nextStates.Add((r+1, c, 1));
                }
                // Rotate counterclockwise (vertical to horizontal)
                if (c + 1 < n && grid[r][c+1] == 0 && grid[r+1][c+1] == 0) {
                    nextStates.Add((r, c, 0));
                }
            }
            
            foreach (var (nr, nc, ndir) in nextStates) {
                if (!visited.Contains((nr, nc, ndir))) {
                    visited.Add((nr, nc, ndir));
                    queue.Enqueue((nr, nc, ndir, steps + 1));
                }
            }
        }
        
        return -1;
    }
}
var minimumMoves = function(grid) {
    const n = grid.length;
    const target = `${n-1},${n-2},${n-1},${n-1}`;
    
    const queue = [[0, 0, 0, 1, 0]]; // [r1, c1, r2, c2, moves]
    const visited = new Set();
    visited.add("0,0,0,1");
    
    while (queue.length > 0) {
        const [r1, c1, r2, c2, moves] = queue.shift();
        const state = `${r1},${c1},${r2},${c2}`;
        
        if (state === target) {
            return moves;
        }
        
        // Move right
        if (c1 + 1 < n && c2 + 1 < n && grid[r1][c1 + 1] === 0 && grid[r2][c2 + 1] === 0) {
            const newState = `${r1},${c1 + 1},${r2},${c2 + 1}`;
            if (!visited.has(newState)) {
                visited.add(newState);
                queue.push([r1, c1 + 1, r2, c2 + 1, moves + 1]);
            }
        }
        
        // Move down
        if (r1 + 1 < n && r2 + 1 < n && grid[r1 + 1][c1] === 0 && grid[r2 + 1][c2] === 0) {
            const newState = `${r1 + 1},${c1},${r2 + 1},${c2}`;
            if (!visited.has(newState)) {
                visited.add(newState);
                queue.push([r1 + 1, c1, r2 + 1, c2, moves + 1]);
            }
        }
        
        // Rotate clockwise (horizontal to vertical)
        if (r1 === r2 && r1 + 1 < n && grid[r1 + 1][c1] === 0 && grid[r2 + 1][c2] === 0) {
            const newState = `${r1},${c1},${r1 + 1},${c1}`;
            if (!visited.has(newState)) {
                visited.add(newState);
                queue.push([r1, c1, r1 + 1, c1, moves + 1]);
            }
        }
        
        // Rotate counterclockwise (vertical to horizontal)
        if (c1 === c2 && c1 + 1 < n && grid[r1][c1 + 1] === 0 && grid[r2][c2 + 1] === 0) {
            const newState = `${r1},${c1},${r1},${c1 + 1}`;
            if (!visited.has(newState)) {
                visited.add(newState);
                queue.push([r1, c1, r1, c1 + 1, moves + 1]);
            }
        }
    }
    
    return -1;
};

复杂度分析

复杂度类型分析
时间复杂度O(n²) - 每个状态最多被访问一次,