#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)
{
d[x][y] = 0;
g[x][y] = 1;
q.push({x, y});
PII end = {n - 1, m - 1};
while (q.size())
{
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. 代码
cpp复制代码
#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函数
cpp复制代码
#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;
}