广度优先搜索BFS求最少步数和最短路径(1432、1433、1900、2109、1438、1442、1819、1441)

 题单位置:题单中心-东方博宜OJ

1432. 走出迷宫的最少步数

问题描述

一个迷宫由 R 行 C 列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。

给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。

输入

第一行是两个整数,R 和 C ,代表迷宫的行数和列数。( 1 ≤ R,C ≤ 40 )

接下来是 R 行,每行 C 个字符,代表整个迷宫。空地格子用 . 表示,有障碍物的格子用 # 表示。

迷宫左上角和右下角都是 . 。

输出

输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。

计算步数要包括起点和终点。

样例

输入

5 5

..###

#....

#.#.#

#.#.#

#.#..

输出

9

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m, sum = 0; 
// ma数组用于存储迷宫地图
char ma[105][105]; 
// vis数组用于标记迷宫中每个位置是否已被访问过,0表示未访问,1表示已访问
int vis[105][105]; 
// dir数组表示四个方向的偏移量,分别是右、下、左、上
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; 

// 定义结构体node,用于表示迷宫中的一个位置及其到起点的距离
struct node{
    int x, y, len;
};

// bfs函数用于执行广度优先搜索,寻找从起点(1, 1)到终点(n, m)的最短路径长度
int bfs(){
    // 创建队列q,用于存储待访问的节点
    queue<node> q; 
    // 将起点(1, 1)及其距离(初始为1)加入队列
    q.push({1, 1, 1}); 
    // 标记起点已被访问
    vis[1][1] = 1; 
    while(!q.empty()){
        // 取出队列头部的节点
        node next = q.front(); 
        q.pop();
        for(int i = 0; i < 4; i++){
            // 计算下一个位置的坐标
            int x = next.x + dir[i][0]; 
            int y = next.y + dir[i][1]; 
            // 如果新位置超出迷宫范围、是墙或者已被访问过,则跳过
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y] == '#' || vis[x][y]) continue; 
            // 如果到达终点,返回当前距离加1(因为从起点到终点的距离要算上终点这一步)
            if(x == n && y == m)return next.len + 1; 
            // 标记新位置已被访问
            vis[x][y] = 1; 
            // 将新位置及其距离加入队列
            q.push({x, y, next.len + 1}); 
        }
    }
}

int main(){
    cin >> n >> m; 
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j]; 
        }
    }
    cout << bfs(); 
    return 0;
}


1433. 走出迷宫的最少步数2

问题描述

当你站在一个迷宫里的时候,往往会被错综复杂的道路弄得失去方向感,如果你能得到迷宫地图,事情就会变得非常简单。假设你已经得到了一个 n×m 的迷宫的图纸,请你找出从起点到出口的最短路。

输入

第一行是两个整数 n 和 m (1 ≤ n, m ≤ 100),表示迷宫的行数和列数。

接下来 n 行,每行一个长为 m 的字符串,表示整个迷宫的布局。字符'.'表示空地,'#'表示墙,'S'表示起点'T'表示出口。

输出

输出从起点到出口最少需要走的步数。

样例

输入

3 3

S#T

.#.

...

输出

6

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx, sy 表示起点的坐标,tx, ty 表示终点的坐标
int sx, sy, tx, ty;
// ma 用于存储迷宫地图
char ma[105][105];
// vis 用于标记迷宫中每个位置是否已经被访问过
int vis[105][105];
// dir 定义了四个方向的偏移量,分别是右、下、左、上
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

// 用于表示迷宫中的一个位置及其到起点的距离
struct node{
    int x, y;
    int len;
};

// 广度优先搜索函数,用于寻找从起点到终点的最短路径
int bfs(){
    // 用于进行广度优先搜索
    queue<node> q;
    // 将起点信息(坐标和初始步数 0)加入队列
    q.push({sx, sy, 0});
    // 标记起点已经被访问
    vis[sx][sy] = 1;
    // 当队列不为空时,继续进行搜索
    while(!q.empty()){
        // 取出队列的队首元素
        node next = q.front();
        // 将队首元素从队列中移除
        q.pop();
        // 遍历四个方向
        for(int i = 0; i < 4; i++){
            // 计算下一个位置的坐标
            int x = next.x + dir[i][0], y = next.y + dir[i][1];;
            // 检查下一个位置是否越界、是否为障碍物(用 '#' 表示)或者是否已经被访问过
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y] == '#' || vis[x][y]) continue;
            // 如果下一个位置是终点
            if(x == tx && y == ty)
                // 返回从起点到终点的步数,即当前位置的步数加 1
                return next.len + 1;
            // 标记下一个位置已经被访问
            vis[x][y] = 1;
            // 将下一个位置的信息(坐标和步数)加入队列
            q.push({x, y, next.len + 1});
        }
    }
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
            if(ma[i][j] == 'S')sx = i, sy = j;
            else if(ma[i][j] == 'T')tx = i, ty = j;
        }
    }
    cout << bfs();
    return 0;
}

1900. 采药的最短路径

问题描述

少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由 M×N 个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。

下图 显示了一个迷阵的样例及李逍遥找到仙药的路线。

输入

第1行输入两个非零整数 M 和 N ,两者均不大于 20 。M 表示迷阵行数, N 表示迷阵列数。

接下来有 M 行, 每行包含 N 个字符,不同字符分别代表不同含义:

1) ‘@’:少年李逍遥所在的位置;

2) ‘.’:可以安全通行的方格;

3) ‘#’:有怪物的方格;

4) ‘*’:仙药所在位置。

输出

求李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。

如果他不可能找到仙药, 则输出 −1。

样例

输入

8 8

.@##...#

#....#.#

#.#.##..

..#.###.

#.#...#.

..###.#.

...#.*..

.#...###

输出

10

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx, sy 是起点坐标,tx, ty 是终点坐标
int sx, sy, tx, ty;
char ma[105][105];
int vis[105][105];
// dir 表示四个方向的偏移量
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

// 定义结构体 node 表示位置及到起点的步数
struct node{
    int x, y;
    int len;
};

int bfs(){
    queue<node> q;
    // 将起点入队
    q.push({sx, sy, 0});
    // 标记起点已访问
    vis[sx][sy] = 1;
    // 队列不为空时继续搜索
    while(!q.empty()){
        // 取出队首元素
        node next = q.front();
        q.pop();
        // 遍历四个方向
        for(int i = 0; i < 4; i++){
            // 计算下一个位置坐标
            int x = next.x + dir[i][0];
            int y = next.y + dir[i][1];
            // 若位置不合法或已访问,跳过
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y] == '#' || vis[x][y]) continue;
            // 若到达终点,返回步数
            if(x == tx && y == ty) return next.len + 1;
            // 标记新位置已访问
            vis[x][y] = 1;
            // 将新位置入队
            q.push({x, y, next.len + 1});
        }
    }
    // 未找到路径,返回 -1
    return -1;
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
            if(ma[i][j] == '@') sx = i, sy = j;
            else if(ma[i][j] == '*') tx = i, ty = j;
        }
    }
    cout << bfs();
    return 0;
}

2109. 古希腊之争

问题描述

伯罗奔尼撒战争是以雅典为首的提洛同盟与以斯巴达为首的伯罗奔尼撒联盟之间的一场战争。这场战争从前431年一直持续到前404年,使得绝大多数周边城邦必须加入其中一方的阵营。战争第一阶段,雅典在伯里克利的领导之下,凭借强大的海军,采取陆地上防御在海上进攻的策略。而斯巴达在阿基达摩斯二世的领导之下,率领它令人畏惧的战士进行陆地强攻。两个强邦侧重点不同的军事力量导致了战争第一阶段的僵持局面。

话说,有一天阿基达摩斯二世决定率兵进攻雅典的一个居民点阿提卡,当他们满怀斗志的奔向阿提卡的时候,殊不知他们正走向伯利克里所设下的迷宫陷阱之中。当他们发现时,已为时已晚。

为了早日返回斯巴达,阿基达摩斯二世立即让所有的斯巴达勇士去需找迷宫的出口 E 。现在他们被困在迷宫的 S 点,迷宫中.表示空地,可以通过,#表示墙,不能通过,每次只能向上下左右四个方向移动,每个勇士每移动一个单位距离需要耗费一个单位时间,所有斯巴达勇士的移动速度和方向相同。现在请你计算一下他们所有人要找到迷宫的出口,最少需要时间之和是多少。

PS:假设迷宫中每个点可以容纳的人数没有限制。

输入

第一行输入三个数 n,m,c ,(2 ≤ m ≤ n ≤ 500,100 ≤ c ≤ 10000)分别代表迷宫的长度和宽度,以及被困迷宫的斯巴达勇士数(不包括阿基达摩斯二世)。

下面 m 行每行有 n 个字符用来表示迷宫地图。

详细输入格式见样例。

输出

输出一个整数,表示找到迷宫出口时,所有勇士消耗的最短时间之和,如不能找到出口输出 −1 。

样例

输入

5 5 100

#####

#S..#

#...#

#...E

#####

输出

500

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx 和 sy 表示起点的坐标,tx 和 ty 表示终点的坐标
int sx, sy, tx, ty;
// c 表示每走一步所需的花费
int c;
// ma 用于存储地图信息
char ma[505][505];
// vis 用于标记地图上每个位置是否已经被访问过
int vis[505][505];
// 定义了四个方向的偏移量,分别为右、下、左、上
int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

struct node{
    int x, y;
    int len;
};

int bfs(){
    queue<node> q;
    // 将起点信息(坐标和初始步数 0)加入队列
    q.push({sx, sy, 0});
    // 标记起点已经被访问过
    vis[sx][sy] = 1;
    // 当队列不为空时,继续进行搜索
    while(!q.empty()){
        // 取出队列的队首元素
        node next = q.front();
        // 将队首元素从队列中移除
        q.pop();
        // 遍历四个方向
        for(int i = 0; i < 4; i++){
            // 计算下一个位置的坐标
            int x = next.x + dir[i][0], y = next.y + dir[i][1];
            // 检查下一个位置是否越界、是否为障碍物(用 '#' 表示)或者是否已经被访问过
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y] == '#' || vis[x][y]) continue;
            // 如果下一个位置是终点
            if(x == tx && y == ty)
                // 返回从起点到终点的步数,即当前位置的步数加 1
                return next.len + 1;
            // 标记下一个位置已经被访问过
            vis[x][y] = 1;
            // 将下一个位置的信息(坐标和步数)加入队列
            q.push({x, y, next.len + 1});
        }
    }
    // 如果遍历完队列都没有找到终点,返回 -1 表示没有路径
    return -1;
}

int main(){
    cin >> n >> m >> c;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
            if(ma[i][j] == 'S')
                sx = i, sy = j;
            else if(ma[i][j] == 'E')
                tx = i, ty = j;
        }
    }
    // 调用 bfs 函数,计算从起点到终点的最短路径步数,并将结果存储在 k 中
    int k = bfs();
    // 如果 k 等于 -1,表示没有找到从起点到终点的路径
    if(k == -1)
        // 输出 -1
        cout << k;
    else
        // 否则,输出最短路径步数乘以每步的花费,即总花费
        cout << k * c;
    return 0;
}

1438. 骑士巡游

问题描述

马在中国象棋以日字形规则移动,给定 n×m 大小的棋盘,以及马的初始位置 (x,y) 和目标位置 (s,t),要求不能重复经过棋盘上的同一个点,计算马至少走多少步可以到达目标位置,所有棋盘保证从初始位置到结束位置一定有路径可达。

输入

测试数据包含一行,为六个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,y,s,t 。( 1 ≤ x, s ≤ n ≤ 5,1 ≤ y, t ≤ m ≤ 5)

输出

包含一行,为一个整数,表示马能到达目标位置的最小步数。

样例

输入

3 3 1 1 1 3

输出

2

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx, sy 是起点坐标,tx, ty 是终点坐标
int sx, sy, tx, ty;
// ma 存储地图信息
char ma[505][505];
// vis 标记位置是否被访问
int vis[505][505];
// dir 表示八个方向的偏移量,类似马走“日”字
int dir[8][2] = {{1, 2}, {1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}};

struct node{
    int x, y;
    int len;
};

int bfs(){
    queue<node> q;
    // 将起点入队
    q.push({sx, sy, 0});
    // 标记起点已访问
    vis[sx][sy] = 1;
    // 队列不为空时继续搜索
    while(!q.empty()){
        // 取出队首元素
        node next = q.front();
        q.pop();
        // 遍历八个方向
        for(int i = 0; i < 8; i++){
            // 计算下一个位置坐标
            int x = next.x + dir[i][0];
            int y = next.y + dir[i][1];
            // 若位置不合法或已访问,跳过
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y] == '#' || vis[x][y]) continue;
            // 若到达终点,返回步数
            if(x == tx && y == ty) return next.len + 1;
            // 标记新位置已访问
            vis[x][y] = 1;
            // 将新位置入队
            q.push({x, y, next.len + 1});
        }
    }
    // 未找到路径,返回 -1
    return -1;
}

int main(){
    cin >> m >> n >> sx >> sy >> tx >> ty;
    cout << bfs();
    return 0;
}

1442. 走出迷宫的最短路径

问题描述

有 n×m 的迷宫,该迷宫有一个入口,一个出口。编写一程序打印一条从迷宫入口到出口的最短路径,黑色方块的单元表示走不通(用 1 表示),白色方块的内容表示走的通(用 0 表示)。

只能往上下左右四个方向走,如果有最短路径,保证最短路径一定是唯一的,如果没有路径可以到达,则输出“no way”。

输入

第一行输入 2 个整数 n 和 m ( n 和 m 都是 10∼150 之间的整数),代表迷宫的行数和列数;

接下来 n 行,每行有 m 个整数,1 代表不可走的点,0 代表可走的点;

接下来一行,有 2 个整数 s1​ 和 s2​ 代表入口的坐标;

接下来一行,有 2 个整数 e1​ 和 e2​ 代表出口的坐标;

本题数据上保证出发点和终点的值一定为 0,也就是不存在出发点和终点不能走的情况。

输出

输出从入口到出口的最短路径,如果没有路径可达输出“no way”。

样例

输入

8 5

1 1 1 1 1

0 0 0 0 1

1 1 1 0 1

1 0 0 0 1

1 0 0 1 1

1 0 0 0 1

1 1 1 0 1

1 0 0 0 1

2 1

8 4

输出

(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,3)->(5,3)->(6,3)->(6,4)->(7,4)->(8,4)

解析:由于需要输出最短路径,使用结构体数组模拟队列,其中结构体中 len 记录是前驱节点,最后使用递归输出即可。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx, sy 表示起点的坐标,tx, ty 表示终点的坐标
int sx, sy, tx, ty;
// ma 用于存储地图信息
int ma[505][505];
// vis 用于标记地图上每个位置是否已经被访问过
// head 和 tail 用于模拟队列的头和尾,初始都为 1
int vis[505][505], head = 1, tail = 1;
// dir 定义了四个方向的偏移量,分别为右、下、左、上
int dir[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

// 定义结构体 node,用于表示地图上的一个位置及其到起点的步数,以及前驱节点(通过 len 表示)
struct node{
    int x, y;
    int len;
}q[40000];

// 打印从起点到终点的路径函数
// k 表示当前节点在队列 q 中的位置
void print(int k){
    // 如果当前节点不是起点(步数不为 0),递归打印前驱节点的路径
    if(q[k].len != 0) print(q[k].len);
    // 输出当前节点的坐标
    cout << "(" << q[k].x << "," << q[k].y << ")";
    // 如果不是最后一个节点,输出箭头表示路径方向
    if(k != tail) cout << "->";
}

bool bfs(){
    // 将起点的坐标和步数(初始为 0)存入队列 q 的头节点
    q[head].x = sx;
    q[head].y = sy;
    q[head].len = 0;
    // 标记起点已经被访问(将地图上起点位置的值设为 1)
    ma[sx][sy] = 1;
    // 当队列头位置小于等于队列尾位置时,继续搜索
    while(head <= tail){
        // 遍历四个方向
        for(int i = 0; i < 4; i++){
            // 计算下一个位置的 x 坐标
            int x = q[head].x + dir[i][0];
            // 计算下一个位置的 y 坐标
            int y = q[head].y + dir[i][1];
            // 检查下一个位置是否越界或者已经被访问(地图上该位置的值不为 0)
            if(x < 1 || y < 1 || x > n || y > m || ma[x][y]) continue;
            // 标记下一个位置已经被访问(将地图上该位置的值设为 1)
            ma[x][y] = 1;
            // 队列尾指针后移一位
            tail++;
            // 将下一个位置的坐标和前驱节点(当前头节点的位置)存入队列 q
            q[tail].x = x;
            q[tail].y = y;
            q[tail].len = head;
            // 如果下一个位置是终点
            if(x == tx && y == ty){
                // 调用 print 函数打印路径
                print(tail);
                // 返回 true 表示找到了路径
                return 1;
            }
        }
        // 队列头指针后移一位
        head++;
    }
    // 如果没有找到路径,返回 false
    return 0;
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
        }
    }
    cin >> sx >> sy >> tx >> ty;
    // 调用 bfs 函数寻找路径
    if(bfs()) ;
    else
        // 如果没有找到路径,输出 "no way"
        cout << "no way";
    return 0;
}

1819. 奇怪的电梯

问题描述

有一天我做了一个梦,梦见了一种很奇怪的电梯。

大楼的每一层楼都可以停电梯,而且第 i 层楼 (1 ≤ i ≤ N) 上有一个数字 Ki​(0 ≤ Ki ​≤ N) 。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。

例如: 3,3,1,2,5 代表了 Ki​(K1​=3, K2​=3,…) ,从 1 楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?

输入

共二行。

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

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

输出

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

样例

输入

5 1 5

3 3 1 2 5

输出

3

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n;
// sx 表示起点的位置,tx 表示终点的位置
int sx, tx;
// ma 用于存储每个位置相关的数值
int ma[505];
// vis 用于标记每个位置是否已被访问
int vis[505];

struct node{
    int x;  // 表示位置
    int len;  // 表示从起点到该位置的步数
};

int bfs(){
    queue<node> q;
    // 将起点信息(位置和初始步数 0)加入队列
    q.push({sx, 0});
    // 标记起点已经被访问
    vis[sx] = 1;
    // 当队列不为空时,继续进行搜索
    while(!q.empty()){
        // 取出队列的队首元素
        node next = q.front();
        q.pop();
        // 计算向上(根据当前位置的数值移动)的位置
        int up = next.x + ma[next.x];
        // 计算向下(根据当前位置的数值移动)的位置
        int down = next.x - ma[next.x];
        // 如果当前位置是终点
        if(next.x == tx){
            // 返回从起点到当前位置(即终点)的步数
            return next.len;
        }
        // 如果向上移动后的位置在有效范围内且未被访问
        if(up >= 1 && up <= n && vis[up] == 0){
            // 标记该位置已被访问
            vis[up] = 1;
            // 将该位置及其步数(当前步数加 1)加入队列
            q.push({up, next.len + 1});
        }
        // 如果向下移动后的位置在有效范围内且未被访问
        if(down >= 1 && down <= n && vis[down] == 0){
            // 标记该位置已被访问
            vis[down] = 1;
            // 将该位置及其步数(当前步数加 1)加入队列
            q.push({down, next.len + 1});
        }
    }
    // 如果没有找到从起点到终点的路径,返回 -1
    return -1;
}

int main(){
    cin >> n >> sx >> tx;
    for(int i = 1; i <= n; i++){
        cin >> ma[i];
    }
    // 从起点到终点的最短步数或 -1 表示无路径
    cout << bfs();
    return 0;
}

1441. 骑士牛

问题描述

John用他的一头母牛和Don先生交换了一头“骑士牛”。这头牛有一个独特的能力——在牧场中能像中国象棋中的马一样跑跳(会中国象棋吗?不会?注意:本题不考虑马被“蹩脚”的情况)。

当然,这头牛不能跳到岩石或树上,不过能跳到有牧草的地方。这儿有一个宽为 X,高为 Y 的矩形牧场(1 ≤ X ≤ 150; 1 ≤ Y ≤ 150)。 “骑士牛”和其它牛一样喜欢干草。给你一张包含“骑士牛”出发地和树、岩石、灌木或其它障碍物及大包干草等位置信息的地图,确定“骑士牛”得到干草最少要跳几“跳”。

地图中“骑士牛”出发地用 K表示;障碍物用 * 表示,牧草用 . 表示,干草所在地用 H 表示。这儿有一个示例地图:

骑士牛得到干草的最少步骤在下图中用 ABC…… 表示,最少要跳 5 “跳”(其它的路径可能超过 5 “跳”):

输入

第 1 行: 两个空格隔开的整数: X 和Y。

第 2..Y+1 行: 第 Y−i+2 行包含 X 个没有空格的字符(就像上面的地图一样):表示第 i 行的地图。

输出

一个单独的整数表示最少的得到干草的“跳”数。所有的数据都能得到干草。

样例

输入

10 11

..........

....*.....

..........

...*.*....

.......*..

..*..*...H

*.........

...*...*..

.K........

...*.....*

..*....*..

输出

5

解析:广度优先搜索BFS,通过逐层向外拓展的方式进行搜索,适用于解决等权最短路径,最先到达终点的路线一定是最短路径。

#include <bits/stdc++.h>
using namespace std;

int n, m;
// sx, sy 表示起点(标记为 'K' 的位置)的坐标
int sx, sy;
// tx, ty 表示终点(标记为 'H' 的位置)的坐标
int tx, ty;
// ma 用于存储地图信息
char ma[205][205];
// vis 用于标记地图上每个位置是否已经被访问过
int vis[205][205];
// dir 定义了八个方向的偏移量,类似马走“日”字的移动方向
int dir[8][2] = {{1, 2}, {1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}};

struct node{
    int x, y;  // 表示位置的坐标
    int len;  // 表示从起点到该位置所走的步数
};

int bfs(){
    queue<node> q;
    // 将起点信息(坐标和初始步数 0)加入队列
    q.push({sx, sy, 0});
    // 将起点位置标记为已访问(这里用 '*' 标记)
    ma[sx][sy] = '*';
    // 当队列不为空时,继续进行搜索
    while(!q.empty()){
        // 取出队列的队首元素
        node next = q.front();
        q.pop();
        // 遍历八个方向
        for(int i = 0; i < 8; i++){
            // 计算下一个位置的坐标
            int xx = next.x + dir[i][0], yy = next.y + dir[i][1];
            // 如果下一个位置是终点
            if(xx == tx && yy == ty){
                // 返回从起点到终点的步数,即当前位置的步数加 1
                return next.len + 1;
            }
            // 检查下一个位置是否越界、是否已经被访问(用 '*' 标记)
            // 如果满足这些条件之一,则跳过该位置,继续检查下一个方向
            if(xx < 1 || yy < 1 || xx > n || yy > m || ma[xx][yy] == '*') continue;
            // 将下一个位置的信息(坐标和步数)加入队列
            q.push({xx, yy, next.len + 1});
            // 将下一个位置标记为已访问(用 '*' 标记)
            ma[xx][yy] = '*';
        }
    }
}

int main(){
    cin >> m >> n;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> ma[i][j];
            if(ma[i][j] == 'K') sx = i, sy = j;
            else if(ma[i][j] == 'H') tx = i, ty = j;
        }
    }
    cout << bfs();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值