LeetCode 面试经典 150_图的广度优先搜索_蛇梯棋(93_909_C++_中等)
题目描述:
给你一个大小为 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、因其行走 编号遵循转行交替方式,所以可以将二维数组转换成一维数组,从而简化题目难度。
筛子的投掷点数决定了两个节点之间是否联通
- 假设从 1 节点开始投掷筛子,则与其存在边的节点为[2,3,4,5,6,7],
- 从 2 节点开始投掷筛子,则与其存在边的节点为[3,4,5,6,7,8]
因求达到编号为 n² 的方格 所需的最少掷骰次数。可以联想到广度优先搜索。蛇或梯子是将一个邻接节点进行了扩展
2、复杂度分析:
① 时间复杂度:O(n²),构建 boardID 数组的时间复杂度是 O(n²),BFS 的时间复杂度是。
② 空间复杂度:O(n²),boardID 数组、dist 数组和队列 q 的空间复杂度都是O(n²)。
代码实现
代码实现(思路一(广度优选搜索)):
cpp
class Solution {
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size(); // 获取棋盘的大小 n
vector<int> boardID(n * n, -1); // 创建一维数组 boardID 用于存储棋盘的转换状态
// 将棋盘从二维转换为一维
int idx = 0; // boardID 的索引
bool leftToRight = true; // 控制当前行的遍历方向,先从左到右,再从右到左
// 遍历棋盘并填充一维数组 boardID
for (int i = n - 1; i >= 0; i--) { // 从棋盘的最后一行开始遍历
if (leftToRight) {
for (int j = 0; j < n; j++) {
boardID[idx++] = board[i][j]; // 从左到右填充 boardID
}
} else {
for (int j = n - 1; j >= 0; j--) {
boardID[idx++] = board[i][j]; // 从右到左填充 boardID
}
}
leftToRight = !leftToRight; // 每行遍历方向相反
}
// BFS (广度优先搜索) 初始化
queue<int> q; // 队列,用于广度优先搜索
q.push(0); // 从棋盘的起始位置(位置0)开始
vector<int> dist(n * n, -1); // dist 数组用于记录每个位置的最短步数
dist[0] = 0; // 起始位置的步数为0
// 广度优先搜索循环
while (!q.empty()) {
int curr = q.front(); // 获取当前队列中的位置
q.pop(); // 移除队列中的位置
// 如果当前已经到达目标位置,返回当前步数
if (curr == n * n - 1) {
return dist[curr];
}
// 尝试掷骰子,掷出1到6的点数
for (int dice = 1; dice <= 6; dice++) {
int next = curr + dice; // 计算掷骰子后的下一个位置
// 如果下一个位置超出棋盘范围,跳出循环
if (next >= n * n) break;
// 如果当前位置上有梯子或蛇,跳到目标位置
if (boardID[next] != -1) {
next = boardID[next] - 1; // 需要减去1是因为 boardID 的索引是从0开始
}
// 如果该位置尚未访问过,更新步数并加入队列
if (dist[next] == -1) {
dist[next] = dist[curr] + 1; // 当前步数 + 1
q.push(next); // 将下一个位置加入队列
}
}
}
// 如果无法到达目标位置,返回-1
return -1;
}
};
以思路一为例进行调试
cpp
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
class Solution {
public:
int snakesAndLadders(vector<vector<int>>& board) {
int n = board.size(); // 获取棋盘的大小 n
vector<int> boardID(n * n, -1); // 创建一维数组 boardID 用于存储棋盘的转换状态
// 将棋盘从二维转换为一维
int idx = 0; // boardID 的索引
bool leftToRight = true; // 控制当前行的遍历方向,先从左到右,再从右到左
// 遍历棋盘并填充一维数组 boardID
for (int i = n - 1; i >= 0; i--) { // 从棋盘的最后一行开始遍历
if (leftToRight) {
for (int j = 0; j < n; j++) {
boardID[idx++] = board[i][j]; // 从左到右填充 boardID
}
} else {
for (int j = n - 1; j >= 0; j--) {
boardID[idx++] = board[i][j]; // 从右到左填充 boardID
}
}
leftToRight = !leftToRight; // 每行遍历方向相反
}
// BFS (广度优先搜索) 初始化
queue<int> q; // 队列,用于广度优先搜索
q.push(0); // 从棋盘的起始位置(位置0)开始
vector<int> dist(n * n, -1); // dist 数组用于记录每个位置的最短步数
dist[0] = 0; // 起始位置的步数为0
// 广度优先搜索循环
while (!q.empty()) {
int curr = q.front(); // 获取当前队列中的位置
q.pop(); // 移除队列中的位置
// 如果当前已经到达目标位置,返回当前步数
if (curr == n * n - 1) {
return dist[curr];
}
// 尝试掷骰子,掷出1到6的点数
for (int dice = 1; dice <= 6; dice++) {
int next = curr + dice; // 计算掷骰子后的下一个位置
// 如果下一个位置超出棋盘范围,跳出循环
if (next >= n * n) break;
// 如果当前位置上有梯子或蛇,跳到目标位置
if (boardID[next] != -1) {
next = boardID[next] - 1; // 需要减去1是因为 boardID 的索引是从0开始
}
// 如果该位置尚未访问过,更新步数并加入队列
if (dist[next] == -1) {
dist[next] = dist[curr] + 1; // 当前步数 + 1
q.push(next); // 将下一个位置加入队列
}
}
}
// 如果无法到达目标位置,返回-1
return -1;
}
};
int main(int argc, char const *argv[])
{
vector<vector<int>> 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}};
Solution s;
cout<<s.snakesAndLadders(board);
return 0;
}
LeetCode 面试经典 150_图的广度优先搜索_蛇梯棋(93_909)原题链接
欢迎大家和我沟通交流(✿◠‿◠)