给你一个大小为 n x n
的整数矩阵 board
,方格按从 1
到 n2
编号,编号遵循 转行交替方式,从左下角开始 (即,从 board[n - 1][0]
开始)的每一行改变方向。
你一开始位于棋盘上的方格 1
。每一回合,玩家需要从当前方格 curr
开始出发,按下述要求前进:
- 选定目标方格
next
,目标方格的编号在范围[curr + 1, min(curr + 6, n2)]
。- 该选择模拟了掷 六面体骰子 的情景,无论棋盘大小如何,玩家最多只能有 6 个目的地。
- 传送玩家:如果目标方格
next
处存在蛇或梯子,那么玩家会传送到蛇或梯子的目的地。否则,玩家传送到目标方格next
。 - 当玩家到达编号
n2
的方格时,游戏结束。
如果 board[r][c] != -1
,位于 r
行 c
列的棋盘格中可能存在 "蛇" 或 "梯子"。那个蛇或梯子的目的地将会是 board[r][c]
。编号为 1
和 n2
的方格不是任何蛇或梯子的起点。
注意,玩家在每次掷骰的前进过程中最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,玩家也 不能 继续移动。
- 举个例子,假设棋盘是
[[-1,4],[-1,3]]
,第一次移动,玩家的目标方格是2
。那么这个玩家将会顺着梯子到达方格3
,但 不能 顺着方格3
上的梯子前往方格4
。(简单来说,类似飞行棋,玩家掷出骰子点数后移动对应格数,遇到单向的路径(即梯子或蛇)可以直接跳到路径的终点,但如果多个路径首尾相连,也不能连续跳多个路径)
返回达到编号为 n2
的方格所需的最少掷骰次数,如果不可能,则返回 -1
。
示例 1:
输入:board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]]
输出:4
解释:
首先,从方格 1 [第 5 行,第 0 列] 开始。
先决定移动到方格 2 ,并必须爬过梯子移动到到方格 15 。
然后决定移动到方格 17 [第 3 行,第 4 列],必须爬过蛇到方格 13 。
接着决定移动到方格 14 ,且必须通过梯子移动到方格 35 。
最后决定移动到方格 36 , 游戏结束。
可以证明需要至少 4 次移动才能到达最后一个方格,所以答案是 4 。
示例 2:
输入:board = [[-1,-1],[-1,3]]
输出:1
提示:
n == board.length == board[i].length
2 <= n <= 20
board[i][j]
的值是-1
或在范围[1, n2]
内- 编号为
1
和n2
的方格上没有蛇或梯子
步骤1:定义计算问题性质
-
输入:
- 一个大小为 n×nn \times nn×n 的二维整数矩阵
board
。 - 矩阵中的每个元素要么是
-1
(无蛇或梯子),要么是一个整数,表示目标方格的编号。 board[i][j]
表示棋盘第 iii 行第 jjj 列格子的状态。
- 一个大小为 n×nn \times nn×n 的二维整数矩阵
-
输出:
- 达到编号为 n2n^2n2 的方格所需的最少掷骰次数。如果无法到达,返回
-1
。
- 达到编号为 n2n^2n2 的方格所需的最少掷骰次数。如果无法到达,返回
-
约束条件:
- 2≤n≤202 \leq n \leq 202≤n≤20。
- 每次掷骰子后,玩家最多可以前进 6 格。
- 蛇和梯子的起点和终点在棋盘上有对应编号。编号 111 和 n2n^2n2 的格子上没有蛇或梯子。
-
潜在边界条件:
- 棋盘上没有蛇或梯子。
- 棋盘中存在死循环(多个蛇或梯子互相连接)。
- n=2n = 2n=2 或 n=20n = 20n=20 的极限大小。
步骤2:问题分解与算法设计
1. 将棋盘映射为一维数组
- 二维矩阵 boardboardboard 的格子编号从左下角开始,转行交替方式编号。
- 通过一个辅助函数实现编号和坐标的相互映射,便于后续算法处理。
2. 采用广度优先搜索(BFS)
- 核心思路:
- BFS 模拟骰子的移动过程,计算从起点 111 到终点 n2n^2n2 的最少步数。
- BFS 保证了搜索路径中每一步都是最短路径。
- 步骤:
- 从起点 111 开始。
- 模拟掷骰子,每次掷骰后计算目标方格(最多前进 6 格)。
- 检查目标方格是否有蛇或梯子,更新位置。
- 如果到达终点 n2n^2n2,记录步数并返回。
- 如果无法继续前进,返回
-1
。
3. 复杂度分析
- 时间复杂度:O(n2)O(n^2)O(n2),每个节点最多访问一次。
- 空间复杂度:O(n2)O(n^2)O(n2),用于存储访问状态。
步骤3:C++代码实现
cpp
class Solution {
public:
// 辅助函数:将二维坐标映射到一维编号
int getId(int row, int col, int n) {
if (row % 2 == n % 2) {
return row * n + (n - 1 - col);
} else {
return row * n + col;
}
}
// 主函数:计算最少掷骰次数
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size();
vector<int> flatBoard(n * n + 1, -1); // 将棋盘展平成一维数组
int id = 1;
for (int i = n - 1; i >= 0; --i) {
for (int j = 0; j < n; ++j) {
int col = (i % 2 == n % 2) ? n - 1 - j : j;
flatBoard[id++] = board[i][col];
}
}
queue<pair<int, int>> q; // 队列存储(当前位置,步数)
vector<bool> visited(n * n + 1, false);
q.push({1, 0}); // 从起点 1 开始
visited[1] = true;
while (!q.empty()) {
auto [curr, steps] = q.front();
q.pop();
for (int dice = 1; dice <= 6; ++dice) {
int next = curr + dice;
if (next > n * n) break; // 超出棋盘范围
if (flatBoard[next] != -1) {
next = flatBoard[next]; // 遇到蛇或梯子
}
if (next == n * n) {
return steps + 1; // 到达终点
}
if (!visited[next]) {
visited[next] = true;
q.push({next, steps + 1});
}
}
}
return -1; // 无法到达终点
}
};
步骤4:解题启发
-
BFS 的优越性:
- 广度优先搜索在寻找最短路径问题中十分高效,尤其适合树形或图形结构。
- 在飞行棋或迷宫问题中,BFS 是一个基础且可靠的算法。
-
问题分解的重要性:
- 本题中棋盘的编号映射是核心,分解问题后算法实现更直观。
步骤5:实际应用场景
-
应用场景:飞行棋游戏优化
- 例如:自动驾驶飞行棋 AI 玩家决策。
- 实现方法:
- 使用 BFS 预先计算最优路径。
- 根据当前棋盘状态调整 AI 策略。
-
物流调度路径规划
- 本算法思想可用于模拟物流配送中的路径规划,特别是有额外加速或延迟条件的复杂网络。