Acwing yyds
BFS(宽度优先遍历)
与深度优先搜索一条路走的死的方式不同,宽度优先遍历是一层一层来搜,当一层的节点被搜索完成后,才会进入下一层。因此可以利用BFS来找到最短路径(当路径权值为1的时候)。接下来用两个例题展示。
走迷宫
思路:从起点开始遍历能走到的点,然后再继续找到能走到的点。以上图为例起点是(0,0),遍历到能够走到的点为(1,0)只有一个。然后继续遍历可走的点(2,0)也只有一个。继续往下(2,1)和(3,0)有两个点可以走。这时就要分别遍历这两个点可以走的路径了。后面过程同上,最后看哪条路径先到达终点,输出最短距离。
要注意的是每一个点只能用一次(走到的点会刷新状态),并且权值相同,这也就保证先走到终点的一定是最短路。
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int n, m;
int g[N][N]; //存放地图
int d[N][N]; //存放点到原点的距离
int bfs()
{
queue<PII> q; //存放可走节点的位置信息
memset(d, -1, sizeof d); //初始化距离为-1,即还没走都该点
d[0][0] = 0; //从起点开始出发
q.push({0, 0}); //往队列加入起点坐标
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //上下左右平移
while (q.size()) //队列不为空,若为空则代表节点已全部遍历完
{
//一次只搜一个节点
auto t = q.front(); //取出队头元素
q.pop(); //弹出对头元素
for (int i = 0; i < 4; i ++ ) //循环四次,对应四个方向的平移
{
int x = t.first + dx[i], y = t.second + dy[i]; //(x,y)对应平移后的坐标
if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) //判断是否出界,是否为通路,以及该点是否已被使用过。
{
d[x][y] = d[t.first][t.second] + 1; //距离+1
q.push({x, y}); //将(x,y)加入队列,等待下一次宽搜
}
}
}
return d[n - 1][m - 1]; //返回最短距离
}
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() << endl;
return 0;
}
八数码
思路:本质上和最短路相同,每一次交换位置的权值都是1,交换过后的方阵就是一个节点。要找到先到达‘1 2 3 4 5 6 7 8 x’(终点)的路径。
难点1:如何表示方阵的变化——用字符串来表示方阵,用find()函数找到'x'的位置记为k。映射到方阵上对应的坐标就是 x = k / 3, y = k % 3。
难点2:如何记录距离——创建一个关联容器(字典),键表示方阵,值表示距离。
#include<iostream>
#include<cstring>
#include<queue>
#include<unordered_map>
using namespace std;
int bfs(string st)
{
queue<string> q;
unordered_map<string, int> d; //创建字典(方阵名,距离)
string end = "12345678x"; //录入终点
q.push(st); //将起点加入队列
d[st] = 0;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
while(q.size())
{
auto t = q.front();
q.pop();
int distance = d[t]; //取出该方阵到起点的距离
if( t == end) return d[t]; //判断该方阵是否是终点,若是返回最短距离
int k = t.find('x'); //找到x的位置
int x = k / 3, y = k % 3; //映射到方阵
for(int i = 0; i < 4; i ++) //遍历(x,y)的四个方向
{
int a = x + dx[i];
int b = y + dy[i]; //(a,b)为(x,y)平移后的坐标
if( a >= 0 && a < 3 && b >= 0 && b < 3) //判断是否出界
{
swap(t[k], t[ a * 3 + b]); //交换对应位置上的值,产生新的节点(方阵)
if(!d.count(t)) //若该节点之前没被遍历过,则距离+1
{
d[t] = distance + 1;
q.push(t);
}
swap(t[k], t[ a * 3 + b]); //恢复状态
}
}
}
return -1; //找不到答案,返回-1
}
int main()
{
char s[2];
string st;
for(int i = 0; i < 9 ; i ++)
{
cin>>s;
st += *s;
}
cout<<bfs(st);
}