LeetCode 面试经典 150_图的广度优先搜索_蛇梯棋(93_909_C++_中等)(广度优选搜索)

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)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
资深web全栈开发2 小时前
LeetCode 3578:统计极差最大为 K 的分割方式数 - 深入浅出指南
算法·leetcode·前缀和·动态规划·滑动窗口
进击的荆棘2 小时前
C++起始之路——类和对象(上)
开发语言·c++
踏浪无痕2 小时前
三周手撸企业级认证系统(二) Spring Security + JWT 完整实战
spring boot·面试·架构
草莓熊Lotso2 小时前
《算法闯关指南:动态规划算法--斐波拉契数列模型》--04.解码方法
c++·人工智能·算法·动态规划
alphaTao2 小时前
LeetCode 每日一题 2025/12/1-2025/12/7
数据库·算法·leetcode
1nv1s1ble2 小时前
[c++] cpp快速添加sqlite_orm
c++·sqlite
苏小瀚2 小时前
[算法]---分治-快排和归并
java·算法·leetcode
Jac_kie_層樓2 小时前
力扣hot100刷题记录(12.1)
算法·leetcode·职场和发展
陶陶name2 小时前
Metal Compute Pipeline:Metal-C++ 环境配置与简单算子实现
开发语言·c++