01 BFS/双端队列 BFS
ToastBread · · 算法·理论
OIWiki
Part 1 什么是 01 BFS
边权值为可能有,也可能没有(由于 BFS 适用于权值为 1 的图,所以一般权值是 0 或 1),或者能够转化为这种边权值的最短路问题。
例如在走迷宫问题中,你可以花 1 个金币走 5 步,也可以不花金币走 1 步,这就可以用 0-1 BFS 解决。
01 BFS,又称双端队列 BFS,是一种仅适用于边权为
01 BFS 一般使用双端队列 deque 解决。
Part 2 01 BFS 的原理
考虑双端队列实现。
01 BFS 使用“0先1后”的优化方法来保证队列内有序。
也即:
- 若选择边权为 0 的边,则将结果储存在双端队列的前方。
- 若选择边权为 1 的边,则将结果储存在双端队列的后方。
01 BFS 具备着“第一次到某个点所需要的代价就是这个点的最小代价”的性质。
有序性的证明
证明:
初始时队列仅有一个元素
对于每一次操作:
我们记每一次操作时的队首为
- 选择 0,将
x 放入队首。 - 选择 1,将
x+1 放入队尾。
显然,此时队列里仅含
Part 3 01 BFS 的实现
稍微改了一下 OIWiki 上的伪代码:
while (队列不为空) {
int u = 队首;
弹出队首;
if (u 满足最终状态) {
结束;
}
for (枚举 u 的邻居) {
更新数据
if (...)
添加到队首;
else
添加到队尾;
}
}
搜索失败;
实际上写起来和普通的 BFS 大同小异,只不过要注意把 queue 的语法改成 deque 的语法。
Part 4 01 BFS 的例题
SP22393
题目简述:
- 现在你在一个
n\times m 的棋盘上,你在(1,1) ,要去(n,m) 。- 你每次可以移动到你四周的方格。如果你目前所在的方格对应的字母与你要去的方格的字母不同,那么这次移动产生
1 的代价,否则不产生。- 求出去往
(n,m) 的最小代价。
典型的 01 BFS。
考虑每次向四周,按照题目要求的求出代价。
回顾上文提到过的特征,第一次到终点的代价就是最小代价。
到此,你就可以尝试自己完成了。下面给出 AC 代码:
#include<bits/stdc++.h>
using namespace std;
struct point
{
int x,y;
};
deque<point> dq,emp;
int n,m,p[1005][1005],dis[1005][1005],t;
void bfs()
{
while(!dq.empty())
{
point f=dq.front();
dq.pop_front();
if(f.x==n&&f.y==m)
{
cout<<dis[n][m]<<endl;
return;
}
point now;
now={f.x+1,f.y};
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]==p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+0)
{
dq.push_front(now);
dis[now.x][now.y]=dis[f.x][f.y];
}
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]!=p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+1)
{
dq.push_back(now);
dis[now.x][now.y]=dis[f.x][f.y]+1;
}
now={f.x-1,f.y};
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]==p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+0)
{
dq.push_front(now);
dis[now.x][now.y]=dis[f.x][f.y];
}
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]!=p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+1)
{
dq.push_back(now);
dis[now.x][now.y]=dis[f.x][f.y]+1;
}
now={f.x,f.y+1};
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]==p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+0)
{
dq.push_front(now);
dis[now.x][now.y]=dis[f.x][f.y];
}
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]!=p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+1)
{
dq.push_back(now);
dis[now.x][now.y]=dis[f.x][f.y]+1;
}
now={f.x,f.y-1};
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]==p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+0)
{
dq.push_front(now);
dis[now.x][now.y]=dis[f.x][f.y];
}
if(now.x>=1&&now.x<=n&&now.y>=1&&now.y<=m&&p[f.x][f.y]!=p[now.x][now.y]&&dis[now.x][now.y]>dis[f.x][f.y]+1)
{
dq.push_back(now);
dis[now.x][now.y]=dis[f.x][f.y]+1;
}
}
cout<<-1<<endl;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m;
char ch;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin>>ch;
p[i][j]=ch;
}
}
memset(dis,0x3f,sizeof(dis));
dis[1][1]=0;
dq=emp;
dq.push_back({1,1});
bfs();
}
}