AT213_E
典型的01BFS,也叫双端队列BFS。
在最基本的BFS中,每次向邻居状态扩展的时候记为“一步 ,我们通过逐层搜索,可以解决从起点到每个状态点的最少步数,这本质上等价于在一个边权为1的图上进行层次遍历,求出每个点距离起点的最短距离(即层数)。每个点(状态)第一次入队,步数即为所求。
经常我们会遇到一类砸墙问题,砸一次墙需要花费1的代价,询问到从起点到终点最少需要砸几次墙。如果抽象出来,就是状态与状态之间要么代价是0,要么代价是1,构成一幅边权是0或者1的图,询问到起点最短路,本质上面可以用最短路路算法求,但是针对这种特殊问题,有更容易的做法。
我们想一想,bfs先走到能走的地方,然后花代价1去砸周边的墙,可以用两个队列实现,进一步抽象,我们可以利用双端队列,完成这个过程。
如果这条分支边权是0的边,从队首进入,如果这个边权是1从队尾进入。循环从队首出队,按照上述过程访问所有邻居状态,直到队空。
队首维护不需要花代价的部分,队尾维护增加代价为1的部分。
时间复杂度:
题目链接
#include<bits/stdc++.h>
using namespace std;
const int N=505;
string s[N];
int h,w;
struct node{
int x,y,step;
};
int vis[N][N];
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
bool check(int i,int j)
{
if(i<0||i>=h||j<0||j>=w)return false;
return true;
}
void bfs()
{
memset(vis,-1,sizeof(vis));
deque<node>que; //双端队列
que.push_back((node){0,0,0});
while(que.size())
{
node t=que.front(); que.pop_front();
int x=t.x,y=t.y,step=t.step;
if(vis[x][y]!=-1)continue;
vis[x][y]=step;
for(int i=0;i<4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(check(xx,yy)&&s[xx][yy]=='.') //将代价为0的邻居状态,插入队首
que.push_front((node){xx,yy,step});
}
for(int i=-2;i<=2;i++)
for(int j=-2;j<=2;j++) //将代价为1的邻居状态,插入队尾
{
if(abs(i)==2&&abs(j)==2)continue; //如果只破环一次,不能走到(x+-2,y+-2)这个格子
int xx=x+i;
int yy=y+j;
if(check(xx,yy)&&s[xx][yy]=='#'&&vis[xx][yy]==-1)
que.push_back((node){xx,yy,step+1});
}
}
}
int main()
{
cin>>h>>w;
for(int i=0;i<h;i++)cin>>s[i];
bfs();
cout<<vis[h-1][w-1];
return 0;
}
类似题目: ABC176D “Wizard in Maze”