title: BFS
tags: ACMer
categories: 搜索
thumbnail: https://gss3.bdstatic.com/84oSdTum2Q5BphGlnYG/timg?wapp&quality=80&size=b150_150&subsize=20480&cut_x=0&cut_w=0&cut_y=0&cut_h=0&sec=1369815402&srctrace&di=0af3d14b50480593f373e4a71dda5c97&wh_rate=null&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fpic%2Fitem%2Fca1349540923dd540360d836d209b3de9c824858.jpg
预备知识
队列
特点
-
先进先出
理解为单向的管道
用数组来模拟队列
头指针往后移一个位置
虚拟的头尾指针
-
从队头删除元素
-
在队尾加入元素
常见操作
- 判断队列是否为空
- 查询队列大小
STL队列的基本用法
push和pop,pull
宽度优先搜索
二叉树的层次遍历
想象一下族谱
维护队列
把孩子加到队列里面
最早扩展到的就是最短路径
hdoj 1548 strange lift
什么时候才能进入队列呢?
其实根本不要以为是深搜,我在做递归什么的。其实并没有,我们要做的只是查找到一个节点,我就把这个节点能搜索到的节点进入队列,因为他会进入到最后面,所以根本不用担心
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAX = 11000;
struct Node //定义一个结构体关于
{
int to, step;
} node, nextnode;
int v[MAX], f[MAX]; //v用来标记
int main()
{
int cas, m, n, i;
while (scanf("%d", &cas) && cas) //cas表示几楼的数据
{
scanf("%d%d", &m, &n); //m表示起点,n表示终点
memset(v, 0, sizeof(v)); //数组初始化
memset(f, 0, sizeof(f));
for (i = 1; i <= cas; i++)
scanf("%d", &f[i]); //输入每层的数据
queue qu; //定义队列
while (!qu.empty()) //清空队列
{
qu.pop();
}
int t1, t2, flag = 0;
node.step = 0; //起始点的步数
node.to = m;
qu.push(node); //把结点的情况输入
v[node.to] = 1; //标记这个点已经走过了
while (!qu.empty())
{
node = qu.front(); //取出首元素
qu.pop();
//下面是三种分类讨论:到达终点,向上走,向下走
if (node.to == n) //如果到达终点
{
printf("%d\n", node.step);
flag = 1;
break;
}
t1 = node.to + f[node.to]; //向上走
if (t1 <= cas && !v[t1]) //条件的控制
{
v[t1] = 1;
nextnode.to = t1;
nextnode.step = node.step + 1;
qu.push(nextnode);
}
t1 = node.to - f[node.to]; //向下走
if (t1 >= 1 && !v[t1])
{
v[t1] = 1;
nextnode.to = t1;
nextnode.step = node.step + 1;
qu.push(nextnode);
}
}
if (!flag) //如果走不到
printf("-1\n");
}
return 0;
}
结构体可以理解为一个圈,里面包含了两个信息
hdoj 1495非常可乐
有几个问题解决不了:
- 怎么判断现在是处于哪个杯子
- 怎么判断它用了这个杯子
二维就够了,因为加起来是固定的。这里指的是我们自己创建的三维判断矩阵
#include
#include
#include
using namespace std;
int v[5];
int sign[110][110][100];
struct cup //记录遍历中3个水杯容藏可乐情况
{
int v[5];
int step;
} temp;
void pour(int a, int b) //倒水函数,把a杯子中的可乐倒到b杯子中
{
int sum = temp.v[a] + temp.v[b];
if (sum >= v[b])
temp.v[b] = v[b];
else
temp.v[b] = sum;
temp.v[a] = sum - temp.v[b];
}
void bfs()
{
int i, j;
queue q;
cup cnt;
cnt.v[1] = v[1];
cnt.v[2] = 0;
cnt.v[3] = 0;
cnt.step = 0;
q.push(cnt);
memset(sign, 0, sizeof(sign));
sign[v[1]][0][0] = 1;
while (!q.empty())
{
cnt = q.front();
q.pop();
if (cnt.v[1] == cnt.v[3] && cnt.v[2] == 0)
{
printf("%d\n", cnt.step);
return;
}
for (i = 1; i < 4; ++i)
{
for (j = 1; j < 4; ++j)
{
if (i != j) //自己不倒水给自己
{
temp = cnt; //每个水位情况都要把所有操作枚举一遍,所以都要赋值为原始水位情况
pour(i, j);
if (!sign[temp.v[1]][temp.v[2]][temp.v[3]])
{
temp.step++;
q.push(temp);
sign[temp.v[1]][temp.v[2]][temp.v[3]] = 1;
}
}
}
}
}
printf("NO\n");
}
int main()
{
while (scanf("%d%d%d", &v[1], &v[2], &v[3]) && v[1] || v[2] || v[3])
{
if (v[2] > v[3])
{
int t = v[2];
v[2] = v[3];
v[3] = t;
}
bfs();
}
return 0;
}
最先出现的目标是最少的次数
隐式图
hdoj 1372 Knight move(模板题)
组成一个移动的数组,循环八次的数组
#include
#include
#include
#include
#define N 10
using namespace std;
int step[N][N][N][N], visit[N][N];
int dir[][2] = {
{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};
struct Node
{
int x, y, s;
};
int bfs(int sx, int sy, int ex, int ey)
{
queue q;
Node head = {sx, sy, 0};
q.push(head);
memset(visit, -1, sizeof(visit));
visit[sx][sy] = 0;
while (q.size())
{
Node f = q.front();
q.pop();
if (f.x == ex && f.y == ey)
return f.s;
for (int i = 0; i < 8; i++)
{
int dx = dir[i][0] + f.x, dy = dir[i][1] + f.y;
if (dx >= 0 && dx < 8 && dy >= 0 && dy < 8 && visit[dx][dy])
{
visit[dx][dy] = 0;
Node t = {dx, dy, f.s + 1};
q.push(t);
}
}
}
}
int main()
{
cin.sync_with_stdio(false);
memset(step, -1, sizeof(step));
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
for (int k = 0; k < 8; k++)
for (int l = 0; l < 8; l++)
if (step[i][j][k][l] != -1)
continue;
else if (i == k && j == l)
step[i][j][k][l] = 0;
else
{
int temp = bfs(i, j, k, l);
step[i][j][k][l] = step[k][l][i][j] = temp;
step[j][i][l][k] = step[l][k][j][i] = temp;
}
char c1, c2;
int x1, y2;
while (cin >> c1 >> x1 >> c2 >> y2)
{
printf("To get from %c%d to %c%d takes %d knight moves.\n", c1, x1, c2, y2, step[c1 - 'a'][x1 - 1][c2 - 'a'][y2 - 1]);
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
typedef pair P;
int next1[8][2]={{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1},{-2,1},{-1,2}};
int num[10][10];
int bfs(int sx,int sy,int ex,int ey)
{
queueq;
while(!q.empty())
q.pop();
q.push(P(sx,sy));
num[sx][sy]=1;
while(!q.empty())
{
P fx=q.front();
if(fx.first==ex&&fx.second==ey)
return num[ex][ey];
for(int i=0;i<8;i++)
{
int nx=fx.first+next1[i][0],ny=fx.second+next1[i][1];
if(nx>=1&&nx<=8&&ny>=1&&ny<=8&&!num[nx][ny])
{
q.push(P(nx,ny));
num[nx][ny]=num[fx.first][fx.second]+1;
}
}
q.pop();
}
}
int main()
{
string ch1,ch2;
int a,b,c,d;
while(cin>>ch1>>ch2)//cin遇空格结束输入。。。。。。
{
memset(num,0,sizeof(num));
b=ch1[0]-'a'+1;a=ch1[1]-'0';d=ch2[0]-'a'+1;c=ch2[1]-'0';
cout<<"To get from "<
hdoj 1312 red and black
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 25;
int w, h;
//int ans;
char maze[maxn][maxn];
int start_x, start_y;
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
struct Node
{
int x, y;
int step;
};
bool check(int x,int y)
{
if(x>=0 && x=0 && y q;
while (!q.empty())
q.pop();
Node now = {start_x,start_y,1};
int ans =1;
q.push(now);
while (!q.empty())
{
Node next = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int dx = next.x + dir[i][0];
int dy = next.y + dir[i][1];
if(maze[dx][dy]=='.' && check(dx,dy))
{
maze[dx][dy] = '#';
Node f ={dx,dy,next.step+1};
ans++;
q.push(f);
}
}
}
return ans;
}
int main()
{
while (~scanf("%d %d", &w, &h) && w+h)
{
start_x = 0;
start_y = 0;
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
cin >> maze[j][i];
if (maze[j][i] == '@')
{
start_x = j;
start_y = i;
}
}
}
cout<
- 把bfs写成函数
- 可以不用写vis数组,如果是迷宫的话可以通过字符的形态来决定有没有走过
- 如果数据不一样可能是x,y反了,试着改一改
hdoj 1253胜利大逃亡
#include
using namespace std;
typedef long long ll;
const int MAXN = 51;
int status[MAXN][MAXN][MAXN];
int vis[MAXN][MAXN][MAXN];
int dir[6][3] = {{1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {-1, 0, 0}, {0, 0, 1}, {0, 0, -1}};
struct Ignatius
{
int x;
int y;
int z;
int step;
};
int main()
{
int A, B, C, T;
int K;
cin >> K;
while (K--)
{
queue q; //队列清空
cin >> A >> B >> C >> T;
Ignatius head = {0, 0, 0, 0};
//memset(status, 0, sizeof(status)); 会在后面输入
memset(vis, 0, sizeof(vis));
q.push(head);
vis[0][0][0] = 1;
for (int i = 0; i < A; i++)
{
for (int j = 0; j < B; j++)
for (int k = 0; k < C; k++)
{
scanf("%d", &status[i][j][k]);
}
}
//int flag = -1;
int ans = -1;
while (!q.empty())
{
Ignatius t = q.front();
q.pop();
if (t.x == A - 1 && t.y == B - 1 && t.z == C - 1)
{
if (t.step <= T)
{
ans = t.step;
break;
}
}
for (int i = 0; i < 6; i++)
{
int dx = t.x + dir[i][0];
int dy = t.y + dir[i][1];
int dz = t.z + dir[i][2];
if (dx < A && dy < B && dz < C && !vis[dx][dy][dz] && status[dx][dy][dz] != 1 && dx >= 0 && dy >= 0 && dz >= 0)
{
vis[dx][dy][dz] = 1;
Ignatius f = {dx, dy, dz, t.step + 1}; // 直接在里面加步数
q.push(f);
}
}
//怎么判断是找不到路还是时间不够
}
cout << ans << endl; //省去了flag的 自己引用t.step的话没有定义
}
return 0;
}
2717 catch that cow
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100010;
int vis[maxn];
int main()
{
int n, k;
while (~scanf("%d %d", &n, &k))
{
memset(vis, 0, sizeof(vis));
queue q;
while (!q.empty())
q.pop();
q.push(n);
vis[n] = 0;
while (!q.empty())
{
int temp = q.front();
if (temp == k)
break;
if (temp + 1 <= 100000 && !vis[temp + 1] && temp + 1 >= 0)
{
q.push(temp + 1);
vis[temp + 1] = vis[temp] + 1;
//ans++; 步数在里面加,因为如果是在外面的话算的是提取队列的次数
}
if (temp - 1 >= 0 && !vis[temp - 1] && temp - 1 <= 100000)
{
q.push(temp - 1);
vis[temp - 1] = vis[temp] + 1;
}
if (temp * 2 <= 100000 && !vis[temp * 2] && temp * 2 >= 0)
{
q.push(temp * 2);
vis[temp * 2] = vis[temp] + 1;
}
q.pop();
}
cout << vis[k] << endl;
}
return 0;
}
- 每次都要清空队列,也就是while不空,就一直pop
- 有些东西可以整合到一起,比如说进队列的过程,可以一起放到循环里面来
- 判断是否满足条件要注意在哪个地方判断,是否会多走一次
- 要包含步数的话,可以写在结构体里面或者用vis数组
hdoj prime path
#include
#include
#include
#include
#include
using namespace std;
char a[5];
char b[5];
bool vis[12][12][12][12];
bool Is_prime[10005];
int ans;
struct Num
{
int num[4]; // 一开始想构造一个函数的
int step;
};
void bfs()
{
memset(vis,0,sizeof(vis));
queue q;
while (!q.empty())
q.pop();
Num start, des;
ans = 0;
for (int i = 0; i < 4; i++)
{
start.num[i] = a[i] - '0';
des.num[i] = b[i] - '0';
}
start.step = 0;
vis[start.num[0]][start.num[1]][start.num[2]][start.num[3]] = 1;
q.push(start);
while (!q.empty())
{
Num now = q.front();
q.pop();
if (now.num[0] == des.num[0] && now.num[1] == des.num[1] && now.num[2] == des.num[2] && now.num[3] == des.num[3])
{
ans = now.step;
return;
}
for (int i = 0; i < 4; i++)
{
if (i == 0)
{
for (int j = 1; j <= 9; j++)
{
int temp = j * 1000 + now.num[1] * 100 + now.num[2] * 10 + now.num[3];
if (!vis[j][now.num[1]][now.num[2]][now.num[3]] && !Is_prime[temp])
{
Num f; //= {j,now.num[1],now.num[1],now.num[1]}
f.num[0] = j;
f.num[1] = now.num[1];
f.num[2] = now.num[2];
f.num[3] = now.num[3];
f.step = now.step + 1;
vis[j][now.num[1]][now.num[2]][now.num[3]] = 1;
q.push(f);
}
}
}
if (i == 1)
{
for (int j = 0; j <= 9; j++)
{
int temp = now.num[0] * 1000 + j * 100 + now.num[2] * 10 + now.num[3];
if (!vis[now.num[0]][j][now.num[2]][now.num[3]] && !Is_prime[temp])
{
Num f; //= {j,now.num[1],now.num[1],now.num[1]}
f.num[0] = now.num[0];
f.num[1] = j;
f.num[2] = now.num[2];
f.num[3] = now.num[3];
f.step = now.step + 1;
vis[now.num[0]][j][now.num[2]][now.num[3]] = 1;
q.push(f);
}
}
}
if (i == 2)
{
for (int j = 0; j <= 9; j++)
{
int temp = now.num[0] * 1000 + now.num[1] * 100 + j * 10 + now.num[3];
if (!vis[now.num[0]][now.num[1]][j][now.num[3]] && !Is_prime[temp])
{
Num f; //= {j,now.num[1],now.num[1],now.num[1]}
f.num[0] = now.num[0];
f.num[1] = now.num[1];
f.num[2] = j;
f.num[3] = now.num[3];
f.step = now.step + 1;
vis[now.num[0]][now.num[1]][j][now.num[3]] = 1;
q.push(f);
}
}
}
if (i == 3)
{
for (int j = 0; j <= 9; j++)
{
int temp = now.num[0] * 1000 + now.num[1] * 100 + now.num[2] * 10 + j;
if (!vis[now.num[0]][now.num[1]][now.num[2]][j] && !Is_prime[temp])
{
Num f; //= {j,now.num[1],now.num[1],now.num[1]}
f.num[0] = now.num[0];
f.num[1] = now.num[1];
f.num[2] = now.num[2];
f.num[3] = j;
f.step = now.step + 1;
vis[now.num[0]][now.num[1]][now.num[2]][j] = 1;
q.push(f);
}
}
}
}
}
}
int main()
{
int t;
for (int i = 2; i * i <= 10000; i++) // true为非素数
{
if (!Is_prime[i])
for (int j = i * i; j <= 10000; j += i)
{
Is_prime[j] = true;
}
}
cin >> t;
while (t--)
{
scanf("%s %s", a, b);
bfs();
cout<
ACwing 844走迷宫
自己写的代码
#include
#include
#include
#include
#include
using namespace std;
typedef pair> PII;
const int maxn = 100 + 10;
int n, m;
int cnt; // 也要加到结构体里面去,因为这样才能代表走到那个位置用了多少步
int maze[maxn][maxn];
bool st[maxn][maxn];
int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
/*
还可以使用
pair, int> PII;
存数据进去的时候注意{{}, }
*/
struct Cor
{
int x;
int y;
int step;
}cor[maxn];
queue q;
bool check(int i, int j)
{
if(i >= 1 && i <= n && j >= 1 && j <= m) return true;
return false;
}
int bfs(int start_x, int start_y, int end_x, int end_y)
{
st[start_x][start_y] = 1;
q.push({start_x, start_y});
while (!q.empty())
{
//PII now = q.front();
Cor now = q.front();
q.pop();
cnt ++ ;
int x = now.x;
int y = now.y;
int step = now.step;
if (x == end_x && y == end_y)
{
return step;
}
for (int i = 0; i < 4; i ++ )
{
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if(!st[dx][dy] && maze[dx][dy] != 1 && check(dx, dy))
{
st[dx][dy] = 1;
q.push({dx, dy, step + 1});
// 不支持这个语法的话,就创建一个结构体然后把结构体push进去
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
scanf("%d", &maze[i][j]);
}
}
cout << bfs(1, 1, n, m) << endl;
}
y总代码
#include
#include
#include
#include
using namespace std;
typedef pair PII;
const int N = 110;
int n, m;
int g[N][N], d[N][N];
int bfs()
{
queue q;
memset(d, -1, sizeof d);
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(); // 自动得到t的类型
q.pop();
for (int i = 0; i < 4; i ++ )
{
int x = t.first + dx[i], y = t.second + dy[i];
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;
q.push({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;
}
BFS总结
生成的状态放到数组里面
优先队列priority queue
hdoj 1242 rescue
优先队列方法
#include
#include
#include
#include
#include
#define MAXN 10000
using namespace std;
char map[1100][1100];
int vis[1100][1100];
int n, m;
int mi;
int sx, sy, ex, ey;
int dx[5] = {1, -1, 0, 0};
int dy[5] = {0, 0, 1, -1};
struct st
{
int x;
int y;
int time;
bool friend operator<(st a, st b)
{
return a.time > b.time;
}
};
bool judge(st a)
{
if (a.x < 0 || a.x >= n || a.y < 0 || a.y >= m || map[a.x][a.y] == '#' || vis[a.x][a.y])
return false;
return true;
}
int bfs(int x, int y)
{
priority_queue q;
while (!q.empty())
q.pop();
st a;
a.x = x;
a.y = y;
a.time = 0;
vis[x][y] = 1;
q.push(a);
while (!q.empty())
{
st b = q.top();
q.pop();
if (map[b.x][b.y] == 'a')
return b.time;
for (int i = 0; i < 4; i++)
{
st end;
end.x = b.x + dx[i];
end.y = b.y + dy[i];
end.time = b.time;
if (judge(end))
{
if (map[end.x][end.y] == 'x')
end.time++;
end.time++;
q.push(end);
vis[end.x][end.y] = 1;
}
}
}
return 0;
}
int main()
{
while (~scanf("%d%d", &n, &m))
{
memset(map, 0, sizeof(map));
for (int i = 0; i < n; i++)
{
scanf("%s", map[i]);
}
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (map[i][j] == 'r')
{
memset(vis, 0, sizeof(vis));
int ans = bfs(i, j);
if (ans < mi)
mi = ans;
}
}
memset(vis, 0, sizeof(vis));
if (mi == 0)
printf("Poor ANGEL has to stay in the prison all his life.\n");
else
printf("%d\n", mi);
}
return 0;
}
#include
#include
#include
#include
#include
#define MAXN 10000
using namespace std;
char map[1100][1100];
int vis[1100][1100];
int n, m;
int sx, sy, ex, ey;
int dx[5] = {1, -1, 0, 0};
int dy[5] = {0, 0, 1, -1};
struct st
{
int x;
int y;
int time;
bool friend operator<(st a, st b)
{
return a.time > b.time;
}
};
bool judge(st a)
{
if (a.x < 0 || a.x >= n || a.y < 0 || a.y >= m || map[a.x][a.y] == '#' || vis[a.x][a.y])
return false;
return true;
}
int bfs(int x, int y)
{
priority_queue q;
while (!q.empty())
q.pop();
st a;
a.x = x;
a.y = y;
a.time = 0;
vis[x][y] = 1;
q.push(a);
while (!q.empty())
{
st b = q.top();
q.pop();
if (map[b.x][b.y] == 'a')
return b.time;
for (int i = 0; i < 4; i++)
{
st end;
end.x = b.x + dx[i];
end.y = b.y + dy[i];
end.time = b.time;
if (judge(end))
{
if (map[end.x][end.y] == 'x')
end.time++;
end.time++;
q.push(end);
vis[end.x][end.y] = 1;
}
}
}
return 9999999;
}
int main()
{
while (~scanf("%d%d", &n, &m))
{
int mi=9999999;
memset(map, 0, sizeof(map));
for (int i = 0; i < n; i++)
{
scanf("%s", map[i]);
}
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (map[i][j] == 'r')
{
memset(vis, 0, sizeof(vis));
int ans = bfs(i, j);
if (ans < mi)
mi = ans;
}
}
memset(vis, 0, sizeof(vis));
if (mi == 9999999)
printf("Poor ANGEL has to stay in the prison all his life.\n");
else
printf("%d\n", mi);
}
return 0;
}
老刘说了一句话,你们在掉了几百块钱很伤心,但是很多人不来我的课。其实我讲课几百块钱一小时。你想交钱我还不一定教
定义好一类规则就行了,长得最帅的出去,全部都出去了
优先队列其实是一个大顶堆,什么是堆呢?堆就是一个拟满二叉树,最后一层的上面全是满的。最后一层全部靠左
前缀编码里面没有前缀
我去找老师---学生找到我
在一个命名空间可以少打两次
greater表示小顶堆
硬着头皮写,问同学
不理解不要写上去,为每一句话负责
收获信息港
剪枝优化,动态规划和搜索都是最优的子问题
https://www.cnblogs.com/ECJTUACM-873284962/p/6750320.html
hdoj 1053 https://blog.csdn.net/dzyhenry/article/details/8887956
1242 https://blog.csdn.net/sdc1992/article/details/7875813
2717 https://blog.csdn.net/rnzhiw/article/details/81414837
1612 https://blog.csdn.net/simoncoder/article/details/44803559