前言

前几天我带大家练习了搜索算法的入门题目,今天我们来看一道洛谷上面稍微有点小难度的题目。实际上搜索算法题的难度全在读懂题目上,读懂题目之后,我们很容易地可以判断出是使用BFS还是DFS,然后套模板即可。好的,废话不多说,我们拿题来看。

题目描述

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1≤i≤N)上有一个数字 Ki​(0≤Ki​≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3,3,1,2,5 代表了 Ki​(K1​=3,K2​=3,……),从 1 楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?

输入格式

共二行。

第一行为三个用空格隔开的正整数,表示 N,A,B(1≤N≤200,1≤A,B≤N)。

第二行为 N 个用空格隔开的非负整数,表示 Ki​。

输出格式

一行,即最少按键次数,若无法到达,则输出 -1

输入输出样例

输入 #1

5 1 5

3 3 1 2 5

输出 #1

3

说明/提示

对于 100% 的数据,1≤N≤200,1≤A,B≤N,0≤Ki​≤N。

本题共 16 个测试点,前 15 个每个测试点 6 分,最后一个测试点 10 分。

题目思考

题目要求求出最少要按几次按钮,我们很容易想到使用BFS,但是BFS的思考难度明显要比DFS大一些,所以我首先尝试使用DFS深度搜索求解了一下,注意因为时间限制,DFS解法只能在洛谷上面过6个测试点,因此DFS解法我就不多讲,附代码如下。

DFS解法

#include <iostream>
#include <cstring>
using namespace std;

const int N = 210;
int n, a, b;
int K[N];  // 记录每一层楼上的数字
bool visited[N];  // 记录是否访问过该楼层
int ans = -1;  // 初始化最小按键次数为 -1,表示无法到达

// 深度优先搜索函数
void dfs(int floor, int steps) {
    // 如果当前楼层超出范围或者已经访问过,直接返回
    if (floor < 1 || floor > n || visited[floor]) return;
    // 标记当前楼层为已访问
    visited[floor] = true;

    // 如果到达目标楼层
    if (floor == b) {
        // 如果是第一次到达目标楼层或者当前步数更少,更新最小按键次数
        if (ans == -1 || steps < ans) {
            ans = steps;
        }
        // 回溯,标记当前楼层为未访问
        visited[floor] = false;
        return;
    }

    // 尝试向上移动
    int up = floor + K[floor - 1];
    dfs(up, steps + 1);

    // 尝试向下移动
    int down = floor - K[floor - 1];
    dfs(down, steps + 1);

    // 回溯,标记当前楼层为未访问
    visited[floor] = false;
}

int main() {
    cin >> n >> a >> b;
    for (int i = 0; i < n; i++) {
        cin >> K[i];
    }

    // 初始化访问标记数组
    memset(visited, false, sizeof(visited));
    // 从起始楼层开始深度优先搜索,初始步数为 0
    dfs(a, 0);

    // 输出最小按键次数
    cout << ans << endl;

    return 0;
}    

BFS解法(标准解法)

BFS队列入队出队示意图(手搓不易,给个关注吧)

BFS代码 (附详细注释)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <utility>  
using namespace std;

// 定义最大楼层数
const int N = 210;
// 定义一个整数对类型,用于存储楼层和步数
typedef pair<int, int> PII;
// f数组用于存储每个楼层的按键值,即按该楼层的按钮后能上升或下降的层数
int f[N];  
// way数组用于标记某个楼层是否已经到达过,避免重复访问
int way[N];  
// n表示总楼层数,a表示起始楼层,b表示目标楼层
int n, a, b;

// 广度优先搜索函数,用于计算从起始楼层a到目标楼层b所需的最少步数
// a: 起始楼层
// b: 目标楼层
// step: 初始步数
int bfs(int a, int b, int step) {
    // 定义一个队列,队列中的元素是PII类型,存储楼层和到达该楼层所需的步数
    queue<PII> q;
    // 将起始楼层和初始步数加入队列
    q.push({a, step});
    // 标记起始楼层已经到达过
    way[a] = 1;  

    // 当队列不为空时,继续搜索
    while (!q.empty()) {
        // 取出队列的队首元素
        PII current = q.front();
        // 将队首元素出队
        q.pop();
        // 如果当前楼层等于目标楼层,返回到达该楼层所需的步数
        if (current.first == b) {
            return current.second;
        }
        // 计算按当前楼层按钮上升后的楼层
        int x = current.first + f[current.first];
        // 判断上升后的楼层是否在有效范围内(大于0且小于等于总楼层数),并且该楼层还未到达过
        if (x > 0 && x <= n && way[x] == 0) {
            // 标记该楼层已经到达过
            way[x] = 1;
            // 将该楼层和到达该楼层所需的步数加1后加入队列
            q.push({x, current.second+1});
        }
        // 计算按当前楼层按钮下降后的楼层
        int y = current.first - f[current.first];
        // 判断下降后的楼层是否在有效范围内(大于0且小于等于总楼层数),并且该楼层还未到达过
        if (y > 0 && y <= n && way[y] == 0) {
            // 标记该楼层已经到达过
            way[y] = 1;
            // 将该楼层和到达该楼层所需的步数加1后加入队列
            q.push({y, current.second+1});
        }
    }
    // 如果无法到达目标楼层,返回 -1
    return -1;
}

int main() {
    // 输入总楼层数n、起始楼层a和目标楼层b
    cin >> n >> a >> b;
    // 循环输入每个楼层的按键值
    for (int i = 1; i <= n; i++) {
        cin >> f[i];
    }
    // 调用bfs函数计算从起始楼层到目标楼层所需的最少步数,并输出结果
    printf("%d", bfs(a, b, 0));
    return 0;
}

 后记

OK,以上就是今天的内容了,注意!!!大家看懂了代码一定要自己亲手再写一遍哈,最后如果我的文章给大家带来了帮助,可以给作者一个关注一个赞,谢谢大家!

 

Logo

集算法之大成!助力oier实现梦想!

更多推荐