算法板子:BFS(广度优先搜索)——迷宫问题,求从迷宫的起点到终点的最短路径; 八数码问题,求从初始布局到最终布局x最少移动多少次
【代码】算法板子:BFS(广度优先搜索)——迷宫问题,求从迷宫的起点到终点的最短路径。
·
目录
1. 核心思想在于bfs函数

2. 代码中用到的数组的含义解释
- 迷宫问题中的g数组标识探测灯照到的点有没有障碍物或者有没有加入队列; 比如g[0][1]=1代表(0,1)这个点有障碍物或者该点加入队列, g[0][1]=0代表(0,1)这个点没有障碍物并且没有加入队列。求最短路径的长度时用到的d数组代表从(0,0)这个点到当前点的距离; 例如d[2][0]=2代表(0,0)到(2,0)的距离为2。q队列放新下标。打印最短路径时用到的pre数组存储一个点的前驱点的下标; 比如pre[0][1]={0,0}代表(0,1)这个点的前驱是(0,0)。
- 八数码问题中的d数组放键值对,键是布局,值是从初始布局变化到该布局x移动了多少次。q队列放新布局。
- 两个问题中相同的数组有dx和dy:dx数组存一个点变到上右下左点的横坐标偏移量; 比如dx[0]=-1代表当前这个点变到上面那个点横坐标减1, dx[1]=0代表从当前点变到右边这个点横坐标不变, dx[2]=1代表变到下面这个点横坐标要加1, dx[3]=0代表变到左边这个点横坐标不变。dy数组存一个点变到上下左右点的纵坐标偏移量; dy[0]=0代表当前点变到上面那个点纵坐标不变, dy[1]=1代表当前点变到右边那个点纵坐标加1, 剩下的dy[2]=0, dy[3]=-1理解类似。
3. 迷宫问题
(1)求从(0,0)点到(4,4)点的最短路径是多少——bfs函数
#include <iostream>
#include <queue>
using namespace std;
const int N = 100 + 10;
typedef pair<int, int> PII;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int g[N][N], d[N][N];
queue<PII> q;
int res, n, m;
int bfs(int x, int y)
{
g[x][y] = 1, d[x][y] = 0, q.push({x, y});
PII end = {n - 1, m - 1};
while (!q.empty())
{
auto u = q.front(); q.pop();
int dis = d[u.first][u.second];
if (u == end) return dis;
for (int i = 0; i < 4; i ++ )
{
int a = u.first + dx[i], b = u.second + dy[i];
if (a < 0 || a == n || b < 0 || b == m) continue;
if (g[a][b]) continue;
g[a][b] = 1, d[a][b] = dis + 1, q.push({a, b});
}
}
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
cin >> g[i][j];
cout << bfs(0, 0) << endl;
return 0;
}
(2)打印最短路径——在bfs函数的基础上多了一个print函数
a. 思想

b. 代码
#include <iostream>
#include <queue>
using namespace std;
const int N = 100 + 10;
int g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
typedef pair<int, int> PII;
PII pre[N][N];
// 往队列中添加探测灯照到的元素
queue<PII> q;
int n, m;
void bfs(int x, int y)
{
// 一开始点在(0,0)上,将(0,0)点作标记并加入队列
g[x][y] = 1;
q.push({x, y});
while (q.size())
{
// 取队头,将队头出队
PII f = q.front();
q.pop();
// 在队头这个点上打探照灯,照到周围四个点,顺序为上右下左
for (int i = 0; i < 4; i ++ )
{
// 照到的点坐标为(a,b)
int a = f.first + dx[i], b = f.second + dy[i];
// 如果照到的点的横坐标或纵坐标越界就跳过该点
if (a < 0 || a == n || b < 0 || b == m) continue;
// 如果照到的这个点有障碍物或者走过就跳过该点
if (g[a][b]) continue;
// 如果照到的点没越界并且没走过或没障碍物,就将照到的点作标记
g[a][b] = 1;
// 记录照到的点的前驱
pre[a][b] = f;
// 将照到的点入队
q.push({a, b});
}
}
}
// 找到最短路径
void print(int x, int y)
{
// 递归到(0,0)这个点了
if (!x && !y)
{
cout << x << " " << y << endl;
return;
}
auto c = pre[x][y];
print(c.first, c.second);
cout << x << " " << y << endl;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n ; i ++ )
for (int j = 0; j < m; j ++ )
cin >> g[i][j];
// 先用bfs将迷宫上所有点遍历一遍
bfs(0, 0);
// 再将终点的下标(4,4)传入print函数,打印最短路径
print(n - 1, m - 1);
return 0;
}
4. 八数码问题——bfs函数
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;
// 往队列里加的是扩展出的新布局
queue<string> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
// 放键值对,键是布局,值是从初始布局变化到该布局x移动了多少次
unordered_map<string, int> d;
// 传入初始布局
int bfs(string init)
{
// 代表从初始布局变到初始布局x移动0次
d[init] = 0;
// 将初始布局加入队列
q.push(init);
// 定义最终布局
string end = "12345678x";
while (q.size())
{
// 取出队头布局
string u = q.front(); q.pop();
// dis代表从初始布局变化到队头布局,x移动了多少次
int dis = d[u];
// 如果取出的队头布局是最终布局,那么输出变化到最终布局时x的移动次数
if (u == end) return d[u];
// 求出x在二维布局中的下标; 也就是先求出x在一维字符串中的下标,再求出x在二维布局中的下标
int k = u.find('x');
int x = k / 3, y = k % 3;
// 探射灯照到x周围4个点,尝试将x与周围点交换
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a == 3 || b < 0 || b == 3) continue;
// 若探射灯照到的这个点没有越界,就交换x和该点,注意是在字符串中交换着两个字符
swap(u[k], u[3 * a + b]);
// 如果交换后的新布局不在map中,代表这个新布局之前没有出现过,那么就在map中加入新布局,并往队列中加入这个新布局
if (!d.count(u)) d[u] = dis + 1, q.push(u);
// 还原回队头布局,x继续交换下一个照到的点,再得到新布局
swap(u[k], u[3 * a + b]);
}
}
// 无法从初始布局变化到最终布局,那么就不存在解决方案
return -1;
}
int main()
{
// init代表初始布局
string init;
char c;
for (int i = 0; i < 9; i ++ ) cin >> c, init += c;
cout << bfs(init) << endl;
return 0;
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)