题目链接909. 蛇梯棋 - 力扣(LeetCode)
题目描述
给定一个 N x N 的棋盘,棋盘的方格按从左下角开始的蛇形方式编号(从 1 开始到 NN)。棋盘中存在蛇和梯子,当玩家到达特定编号的格子时,会被传送到另一个格子。求玩家从起点(编号 1)移动到终点(编号 NN)所需的最少移动次数。
解题思路
这是一个典型的广度优先搜索(BFS)问题。BFS 适合求解最短路径问题,因为它逐层扩展状态,一旦找到目标状态,当前步数即为最短路径。
棋盘的特殊编号方式需要进行坐标转换:
- 从编号到行列的转换需要考虑蛇形排列,即偶数行(从 0 开始)从左到右编号,奇数行从右到左编号。
- 转换函数需要处理这种蛇形排列,确保正确映射到棋盘上的实际位置。
代码实现
#include <vector>
#include <queue>
using namespace std;
class Solution {
// 将编号转换为棋盘上的行列坐标
pair<int, int> convertToBoardPosition(int id, int boardSize) {
int row = (id - 1) / boardSize; // 计算所在行
int col = (id - 1) % boardSize; // 计算所在列
// 处理蛇形排列:偶数行从左到右,奇数行从右到左
if (row % 2 == 1) {
col = boardSize - 1 - col; // 奇数行需要反转列顺序
}
// 棋盘的行是从下往上数的,所以需要转换
return {boardSize - 1 - row, col};
}
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size(); // 棋盘大小
vector<bool> visited(n * n + 1, false); // 记录每个位置是否已访问
queue<pair<int, int>> bfsQueue; // BFS队列,存储{当前位置, 步数}
bfsQueue.push({1, 0}); // 从位置1开始,初始步数为0
visited[1] = true; // 标记起点为已访问
while (!bfsQueue.empty()) {
auto [currentPos, steps] = bfsQueue.front();
bfsQueue.pop();
// 尝试从当前位置移动1到6步
for (int dice = 1; dice <= 6; ++dice) {
int nextPos = currentPos + dice;
// 如果超出棋盘范围,停止扩展
if (nextPos > n * n) {
break;
}
// 转换为棋盘上的行列坐标
auto [row, col] = convertToBoardPosition(nextPos, n);
// 如果该位置有蛇或梯子,更新下一步位置
if (board[row][col] != -1) {
nextPos = board[row][col];
}
// 如果到达终点,返回当前步数+1
if (nextPos == n * n) {
return steps + 1;
}
// 如果下一步位置未访问过,加入队列并标记为已访问
if (!visited[nextPos]) {
visited[nextPos] = true;
bfsQueue.push({nextPos, steps + 1});
}
}
}
// 无法到达终点
return -1;
}
};
代码解释
- convertToBoardPosition 函数:将编号转换为棋盘上的行列坐标,处理蛇形排列的特殊规则。
- visited 数组:记录每个位置是否已被访问,避免重复处理。
- BFS 队列:使用队列进行广度优先搜索,每个元素包含当前位置和到达该位置的步数。
- 主循环:每次从队列中取出当前位置,尝试所有可能的移动(1 到 6 步),处理蛇和梯子的跳转,更新队列。
通过这种方式,我们可以高效地找到从起点到终点的最短路径,或者确定无法到达终点。