前言
参考视频教程洛谷 普及组试炼场 - 广度优先搜索 (BFS)
广度优先搜索 (BFS) - 类似队列的思想,先进先出。 搜过了的就不用再搜索了。
当题目要求最短步骤或路径时,就可考虑用BFS的思想。
以简单基础普及题型为例,可在洛谷上查找题目提交,代码仅供参考。
题目列表:
1.填涂颜色
2.马的遍历
3.奇怪的电梯
4.01迷宫
5.字串变换
6.机器人搬重物
1.填涂颜色
题目描述
由数字00组成的方阵中,有一任意形状闭合圈,闭合圈由数字11构成,围圈时只走上下左右44个方向。现要求把闭合圈内的所有空间都填写成2.例如:6×6的方阵(n=6),涂色前和涂色后的方阵如下:
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数n*(1≤*n≤30)
接下来n行,由0和1组成的n×n的方阵。
方阵内只有一个闭合圈,圈内至少有一个0。
输出格式
已经填好数字2的完整方阵。
输入输出样例
输入 #1复制
6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
输出 #1复制
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
说明/提示
1≤n≤30
代码
#include<iostream>
#include<queue>
using namespace std;
int n;
int vis[40][40]; //记录访问节点
int mp[40][40]; //初始数据
int xx[] = { 0,0,1,-1 };
int yy[] = { 1,-1,0,0 };
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> mp[i][j];
}
}
queue<int> x;
queue<int> y;
x.push(0);
y.push(0);
while (!x.empty())
{
for (int i = 0; i < 4; i++)
{
int dx = x.front() + xx[i];
int dy = y.front() + yy[i];
if (dx >= 0 && dx <= n + 1 && dy >= 0 && dy <= n + 1 && mp[dx][dy] != 1 && vis[dx][dy] == 0)
{
x.push(dx);
y.push(dy);
vis[dx][dy] = 1;
}
}
x.pop();
y.pop();
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (vis[i][j] == 0 && mp[i][j] != 1)
{
cout << "2";
}
else
{
cout << mp[i][j];
}
cout << " ";
}
cout << endl;
}
return 0;
}
2.马的遍历
题目描述
有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
输入格式
一行四个数据,棋盘的大小和马的坐标
输出格式
一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)
输入输出样例
输入 #1复制
3 3 1 1
输出 #1复制
0 3 2
3 -1 1
2 1 4
代码
#include <iostream>
#include <bits/stdc++.h>
#include <queue>
using namespace std;
int mp[420][420];
int xx[] = { 1,2,2,1,-1,-2,-2,-1 }; //马的移动需要知道
int yy[] = { 2,1,-1,-2,-2,-1,1,2 };
struct node
{
int x, y, t;
};
int main()
{
int n, m, sx, sy;
cin >> n >> m >> sx >> sy;
memset(mp, -1, sizeof(mp));
queue<node> q;
node p;
p.x = sx;
p.y = sy;
p.t = 0;
q.push(p);
mp[sx][sy] = 0;
while (!q.empty())
{
for (int i = 0; i < 8; i++)
{
int dx = q.front().x + xx[i];
int dy = q.front().y + yy[i];
if (dx >= 1 && dx <= n && dy >= 1 && dy <= m && mp[dx][dy] == -1)
{
mp[dx][dy] = q.front().t + 1;
node temp;
temp.x = dx;
temp.y = dy;
temp.t = mp[dx][dy];
q.push(temp); // 或者直接强制转换 q.push((node){dx,dy,mp[dx][dy]});
}
}
q.pop();
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cout << left << setw(5) << mp[i][j]; //左对宽
}
cout << endl;
}
return 0;
}
3.奇怪的电梯
题目描述
呵可,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1≤i≤N)上有一个数字K1(0≤压1≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于 当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3,3,1,2,5代表了K1(压1=3K2=3,),从1楼开始。在1楼,按“上"可以到4楼,按”下"是不起作用的,因为没有-2 楼。那么,从A楼到B楼至少要按几次按钮呢?
输入格式
共二行。
第一行为3个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N)N,A,B(1≤N≤200,1≤A,B≤N)。
第二行为N个用空格隔开的非负整数,表示Ki。
输出格式
一行,即最少按键次数,若无法到达,则输出−1。
输入输出样例
输入 #1复制
5 1 5
3 3 1 2 5
输出 #1复制
3
代码
#include <iostream>
#include <queue>
using namespace std;
int y[205];
int vis[205]; //防止返回
int main()
{
int n, a, b;
cin >> n >> a >> b;
for (int i = 1; i <= n; i++)
{
cin >> y[i];
}
queue<int> s;
queue<int> bs;
s.push(a);
bs.push(0);
while (!s.empty())
{
if (s.front() == b)
{
cout << bs.front() << endl;
return 0;
}
int ss = s.front() + y[s.front()];
if (ss <= n && vis[ss] == 0)
{
s.push(ss);
bs.push(bs.front() + 1);
vis[ss] = 1;
}
int ss2 = s.front() - y[s.front()];
if (ss2 >= 1 && vis[ss2] == 0)
{
s.push(ss2);
bs.push(bs.front() + 1);
vis[ss2] = 1;
}
s.pop();
bs.pop();
}
cout << "-1" << endl;
return 0;
}
4.01迷宫
题目描述
有一个仅由数字0与1组成的n×n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
输入格式
第11行为两个正整数n,m。
下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。
接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。
输出格式
m行,对于每个询问输出相应答案。
输入输出样例
输入 #1复制
2 2
01
10
1 1
2 2
输出 #1复制
4
4
代码
#include <iostream>
#include <queue>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, m;
int mp[maxn][maxn];
int vis[maxn][maxn];
int color;
int cnt = 0;
int ans[1000010]; //记录每种颜色可移动的格子数
int xx[] = { 0,1,0,-1 };
int yy[] = { 1,0,-1,0 };
void bfs(int x, int y)
{
queue<int> h;
queue<int> l;
h.push(x);
l.push(y);
vis[x][y] = color; //同一块的区域涂颜色
while (!h.empty())
{
for (int i = 0; i < 4; i++)
{
int dx = h.front() + xx[i];
int dy = l.front() + yy[i];
if (dx <= n && dx >= 1 && dy <= n && dy >= 1 && !vis[dx][dy] && mp[dx][dy] != mp[h.front()][l.front()])
{
vis[dx][dy] = color;
h.push(dx);
l.push(dy);
}
}
h.pop();
l.pop();
cnt++; //用来记录每次弹出的次数,即可移动的格子数
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
char k; //注意是输入字符间无空格
cin >> k;
if (k == '1')
{
mp[i][j] = 1;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (!vis[i][j])
{
color++;
bfs(i, j);
ans[color] = cnt;
cnt = 0;
}
}
}
for (int i = 1; i <= m; i++)
{
int h, l;
cin >> h >> l;
cout << ans[vis[h][l]] << endl;
}
return 0;
}
5.字串变换
题目描述
已知有两个字串 A,B及一组字串变换的规则(至多 6 个规则):
A1 ->B1
A2 -> B2
规则的含义为:在 A 中的子串 A1 可以变换为 B1,A2 可以变换为B2 …。
例如:A=abcd,B=xyz,
变换规则为:
abc→xu,ud→y,y→yz
则此时,A 可以经过一系列的变换变为 B*,其变换的过程为:
abcd→xud→xy→xyz。
共进行了 3次变换,使得 A变换为 B。
输入格式
输入格式如下:
AB
A1 B1
A2B2 |-> 变换规则
… …/
所有字符串长度的上限为 2020。
输出格式
若在 10 步(包含 10 步)以内能将 A 变换为 B,则输出最少的变换步数;否则输出 NO ANSWER!
输入输出样例
输入 #1复制
abcd xyz
abc xu
ud y
y yz
输出 #1复制
3
代码
#include <iostream>
#include <bits/stdc++.h>
#include <queue>
#include <string>
using namespace std;
map<string, int> mp; //神奇工具~ ,记录每个字符串是否为访问过
int main()
{
string a, b;
cin >> a >> b;
string aa[10], bb[10];
int n = 1;
while (cin >> aa[n] >> bb[n])
{
n++;
}
n--;
queue<string> w;
queue<int> bs;
w.push(a);
bs.push(0);
while (!w.empty())
{
if (w.front() == b)
{
cout << bs.front() << endl;
return 0;
}
if (bs.front() == 10)
{
bs.pop();
w.pop();
}
string tt = w.front();
if (mp.count(tt)) // tt 是否出现 ,1 为出现,0为否 ,出现过就没必要继续查它了
{
w.pop();
bs.pop();
continue;
}
mp[tt] = 1;
for (int i = 1; i <= n; i++)
{
int p = 0;
while (tt.find(aa[i], p) <= tt.length()) //从 tt 的下标 p 开始查找a[i],找到则返回找到的第一个字母位置,找不到则返回一个很大的值
{
p = tt.find(aa[i], p);
w.push(tt.substr(0, p) + bb[i] + tt.substr(p + aa[i].length()));
bs.push(bs.front() + 1);
p++; // p++往下移一位继续搜寻 tt 中等于 aa[i] 的片段
}
}
w.pop();
bs.pop();
}
cout << "NO ANSWER!" << endl;
return 0;
}
6.机器人搬重物
题目描述
机器人移动学会(RMI
)现在正尝试用机器人搬运物品。机器人的形状是一个直径1.6米的球。在试验阶段,机器人被用于在一个储藏室中搬运货物。储藏室是一个 N×M 的网格,有些格子为不可移动的障碍。机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方。机器人接受的指令有:向前移动1步(Creep
);向前移动2步(Walk
);向前移动33 步(Run
);向左转(Left
);向右转(Right
)。每个指令所需要的时间为1 秒。请你计算一下机器人完成任务所需的最少时间。
输入格式
第一行为两个正整数N*,M(N,M≤50),下面N行是储藏室的构造,0表示无障碍,1表示有障碍,数字之间用一个空格隔开。接着一行有4个整数和1个大写字母,分别为起始点和目标点左上角网格的行与列,起始时的面对方向(东E,南S*,西W*,北N*),数与数,数与字母之间均用一个空格隔开。终点的面向方向是任意的。
输出格式
一个整数,表示机器人完成任务所需的最少时间。如果无法到达,输出-1。
输入输出样例
输入 #1复制
9 10
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1 0
7 2 2 7 S
输出 #1复制
12
代码
#include <iostream>
#include <bits/stdc++.h>
#include <queue>
using namespace std;
bool mp[60][60];
bool vis[60][60][5]; //方向也要标记
int xx[] = { 0,1,0,-1 };
int yy[] = { 1,0,-1,0 };
struct dian
{
int x, y, t, fx;
};
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int temp;
cin >> temp;
if (temp == 1)
{
mp[i][j] = mp[i - 1][j] = mp[i][j - 1] = mp[i - 1][j - 1] = 1; //注意是一个方格子上的四个点,这想法妙哇
}
}
}
int sx, sy, fx, fy;
char w;
cin >> sx >> sy >> fx >> fy >> w;
dian d;
d.x = sx; d.y = sy; d.t = 0;
if (w == 'E') d.fx = 0; //将字母方向匹配上移动方向数组
if (w == 'S') d.fx = 1;
if (w == 'W') d.fx = 2;
if (w == 'N') d.fx = 3;
queue<dian> q;
vis[d.x][d.y][d.fx] = 1;
q.push(d);
while (!q.empty())
{
dian temp = q.front();
if (temp.x == fx && temp.y == fy)
{
cout << temp.t;
return 0;
}
temp.t++;
temp.fx = (q.front().fx + 1) % 4; //记得取余
if (!vis[temp.x][temp.y][temp.fx])
{
vis[temp.x][temp.y][temp.fx] = 1;
q.push(temp);
}
temp.fx = (q.front().fx + 3) % 4;
if (!vis[temp.x][temp.y][temp.fx])
{
vis[temp.x][temp.y][temp.fx] = 1;
q.push(temp);
}
temp.fx = q.front().fx;
int run = 3;
while (run--) //慢慢一步步走
{
temp.x += xx[temp.fx];
temp.y += yy[temp.fx];
if (!vis[temp.x][temp.y][temp.fx] && !mp[temp.x][temp.y] && temp.x >= 1 && temp.x < n && temp.y >= 1 && temp.y < m)
{
vis[temp.x][temp.y][temp.fx] = 1;
q.push(temp);
}
else
{
break;
}
}
q.pop();
}
cout << "-1";
return 0;
}