BFS
bfs也是一个对连通图进行遍历的算法。它的思想是从一个被选定的点出发;然后从这个点的所有方向齐头并进,每次只向前走一小步;如果得不到目的解,那就返回事先定好的值,如果找到直接返回目的解。
运用队列和函数内循环构造的。
dfs也可以找到最短路径,但是时间复杂度高;而且dfs能找到到终点的路径但是不一定是最短
但是BFS搜索到的结果一定是最短的
作用:
(1):求最短路径 最少操作 最小步数 最小时间 比如:求二叉树最小高度
(2):求联通块(与DFS类似)
结构:
其搜索路线是以一层一层、由近及远的顺序进行搜索,每一次向外扩展一步,最先推展到终点的那条路就是最短路径
理解BFS:
需要使用一个队列来实现一层一层访问节点的顺序
走法:
起始点为 a,终点为 f
求最少经过几个点能从点 a 到达点 f
第一层为起点,即点 a
第二层为点 a 能去到的位置,即点 b、c、d
第三层为点 b、c、d 能去到的位置,即点 e、f
此刻在第三层已经到达了点 f,即到达了终点。
那么我们便可以得出最少经过的点数了,即为 3 个点(a、d、f)
代码框架:
1.准备结构体:
//开一个结构体
struct point
{
int x;//结点所在位置
int y;
int step;//步数
};
2.编写BFS函数(二维)
const int maxn = ...;
int gx, gy; // 终点的位置,在 main 里读入
int n, m; // 图的边界,在 main 里读入
bool vis[maxn][maxn]; // 记录位置是否被走过
int map[maxn][maxn]; // 建一个二维数组来存放图的内容
// 以四个方向为例:上下左右
int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};
int bfs(int x, int y)
{
queue<node>q; // 开一个队列
// 对起点初始化
st.x = x;
st.y = y;
st.step = 0; // 初始层次为 0
memset(vis, false, sizeof(vis));
vis[x][y] = true; // 标记初始位置已被走过
q.push(st); // 先将起点放入队列
while(!q.empty()) // 直到队列为空 或 找到终点后,终止循环
{
st = q.front(); // 将起点更新为队列中的第一个结构
q.pop(); // 然后将队列中第一个结构删除
// 找到终点,就结束函数,并返回此时层数
if(st.x==gx && st.y==gy)
return st.step;
for(int i=0; i<4; i++)//四个方向试探
{
ed.x = st.x + dx[i];
ed.y = st.y + dy[i];
ed.step = st.step + 1;
// 若该位置已走过,则进入下一循环,避免重复
// 若超出边界,则进入下一循环
if(vis[ed.x][ed.y] || map[ed.x][ed.y]=='题目条件' || ed.x<0 || ed.x>=n || ed.y<0 || ed.y>=m)
continue;
vis[ed.x][ed.y] = true; //标记该位置已走过
// 若还未到终点,则将其放入队列,变成下次调用的起点
q.push(ed);
}
}
return -1; // 若找不到终点,则按题目要求返回特定值
}
3.编写main函数
int main()
{
int sx, sy; //起点
scanf("%d %d", &n, &m); //读入边界
scanf("%d %d", &sx, &sy); //读入起点
scanf("%d %d", &gx, &gy); //读入终点
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
scanf("%c", &map[i][j]);
if(sx==gx && sy==gy)
printf("0\n");
else
printf("%d\n", bfs(sx, sy));
return 0;
}
一维
问题描述:奇怪的电梯
有一个奇怪的电梯。电梯可以根据需要在每个楼层停下罐头,每层楼都有一个数字Ki(0 <= Ki <= N)。升降机只有两个按钮:向上和向下。当你在i楼时,如果你下“UP”按钮,你将上Ki楼层,即你将进入i+Ki楼,同样,如果你按下“DOWN”按钮,你将下Ki楼层,即你将去i-Ki楼。当然,电梯不能上升到N以上,也不能下降到低于1。例如,有一个5层的建筑物,k1 = 3,k2 = 3,k3 = 1,k4 = 2,k5 = 5.从1楼开始,你可以按按钮“UP”,你会上到4楼,如果你按下按钮“DOWN”,电梯就做不到了,因为它不能下到-2楼,如您所知,-2楼不存在。
问题来了:当你在A楼,你想去B楼时,他至少要按多少次“上”或“下”按钮?
实际问题:求最少,因此考虑BFS
输入
第一行包含上面描述的三个整数N,A,B(1<= N,A,B <= 200),第二行包含N个整数k1,k2,…kn.
单个 0 表示输入结束。
输出
对于每种情况下的输入输出一个中间器,当你在A楼时,你必须按下按钮的次数最少,你想去B楼,如果你不能到达B楼,printf “-1”。
样例输入
5 1 5
3 3 1 2 5
0
样例输出
3
#include<iostream>
#include <queue>
using namespace std;
//结构体
struct node
{
int x;//坐标:层数
int step;
}st,ed;
queue<node> q;//申请队列
int sx,gx,n;
int a[205];
int v[205];//标记 1走过 0未走过
int bfs(int x)
{
st.x=x;//初始化起点
st.step=0;
v[x]=1;//标记走过
q.push(st);//将起点放入队列,注意这里写的是结构体的
while(!q.empty()){//当队列不为空
//
st=q.front();//将起点放入队列中
q.pop();//从队列中删掉
if(st.x==gx){//到达终点 结束 返回步数
return st.step;
}
for(int i=-1;i<=1;i+=2){
ed.x=st.x+i*a[st.x];//不理解+的后半部分
ed.step=st.step+1;
//如果位置走过,则进入下一层循环,避免重读
//超出边界,则进入下一层循环
if(v[ed.x]==1 || ed.x>n || ed.x<=0){
continue;
}
v[ed.x]=1;//标记已经走过
q.push(ed);//如果还没有到达终点,就放入队列中,作为下一次的起点
}
}
return -1;//题目要求到不了终点输出-1
}
int main()
{
cin>>n>>sx>>gx;//n表示楼层数
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(sx==gx){
cout<<0<<endl;
}
else{
cout<<bfs(sx);//从起点开始
}
return 0;
}
一维
题目描述:
农夫约翰被告知一头逃跑的奶牛的位置,想立即抓住她。他从数线上的点 N(0 ≤ N ≤ 100,000)开始,母牛在同一条数线上的点 K (0 ≤ K ≤ 100,000)。农夫约翰有两种交通工具:步行和传送。
步行:FJ可以在一分钟内
从任何点X移动到点X-1或X + 1
传送:FJ可以在一分钟内从任何点X移动到2×X点。
如果这头牛不知道它的追逐,根本不动,农夫约翰需要多长时间才能取回它?
输入
第 1 行:两个空格分隔的整数:N 和 K
输出
第 1 行:农夫约翰抓住逃跑的牛所需的时间最短,以分钟为单位。
样例输入
5 17
样例输出
4
提示
农夫约翰到达逃逸牛的最快方法是沿着以下路径移动:5-10-9-18-17,需要 4 分钟。
代码:
#include <iostream>
#include <queue>
using namespace std;
//先定义结构体才能申请队列,顺序不能乱
struct node
{
int x;//一条线上的位置
int step;//时间
}st,ed;
queue<node> q;
int v[100005];//1表示已经访问
int sx,gx;
int bfs(int x)
{
//队列
st.x=x;
st.step=0;
v[x]=1;//标记:起点
q.push(st);//将起点放入队列
while(!q.empty())
{
st=q.front();//将起点更新为队列中的第一个结构体
q.pop();
if(st.x==gx){//判断有没有到终点
return st.step;
}
//有三个分支,用for循环
for(int i=0;i<3;i++){
if(i==0){
ed.x=st.x-1;
}
else if(i==1){
ed.x=st.x+1;
}
else if(i==2){
ed.x=st.x*2;
}
ed.step=st.step+1;
//位置访问过 超边界
if( ed.x<0 || ed.x>100005 || v[ed.x]==1)
continue;
v[ed.x]=1;
q.push(ed);
}
}
}
int main()
{
cin>>sx>>gx;
if(sx==gx){
cout<<0<<endl;
}
else{
cout<<bfs(sx);
}
return 0;
}
二维
问题描述:骑士招式
你的一个朋友正在研究旅行骑士问题(TKP),在那里你要找到最短的骑士移动闭合之旅(最短路径),它只访问棋盘上给定n个方块的每个方格一次。他认为问题中最困难的部分是确定两个给定方格之间的最小骑士移动次数,一旦完成此操作,找到游览将很容易。
当然,您知道反之亦然。所以你让他写一个程序来解决“困难”的部分。 你的工作是编写一个程序,将两个正方形a和b作为输入,然后确定从a到b的最短路线上的骑士移动次数。
西洋棋中骑士的走法时:先横或竖1或2格,再竖或横2或1格,不只是走一格 因此骑士可能的走法有8种:
int dx[8] = {-2,-2,2,2,-1,-1,1,1};
int dy[8] = {-1,1,-1,1,-2,2,-2,2};
#include <stdio.h>
#include <queue>
struct node{
int x, y;
int step;
} st, ed;
int gx, gy;
int dx[8] = {-2,-2,2,2,-1,-1,1,1};
int dy[8] = {-1,1,-1,1,-2,2,-2,2};
//最关键!!!
int bfs(int x, int y)
{
std::queue<node>q;
st.step = 0;
st.x = x;
st.y = y;
q.push(st);
while(!q.empty())
{
st = q.front();
q.pop();
if(st.x==gx && st.y==gy)
return st.step;
for(int i=0; i<8; i++)
{
ed.x = st.x + dx[i];
ed.y = st.y + dy[i];
ed.step = st.step + 1;
//边界
if(ed.x<=0 || ed.x>8 || ed.y<=0 || ed.y>8) continue;
q.push(ed);
}
}
return -1;
}
int main()
{
int sx, sy;
char c1, c2;
while(scanf("%c%d %c%d", &c1, &sy, &c2, &gy) != EOF)
{
getchar();
sx = c1 - 'a' + 1;
gx = c2 - 'a' + 1;
if(sx==gx && sy==gy)
printf("To get from %c%d to %c%d takes 0 knight moves.\n", c1, sy, c2, gy);
else
printf("To get from %c%d to %c%d takes %d knight moves.\n",c1, sy, c2, gy, bfs(sx, sy));
}
return 0;
}