相较于深度搜索的一条路走到黑,广度搜索如其名是逐层搜索,一次把整一层搜完。我们用一个队列来实现,每次取出队头,对于队头状态的所有分支,把沿着分支所能达到的下一个状态(如果这个状态没被访问过或者可以形成更优的解)插入队尾,直到队列为空。
【例题】Bloxorz(poj3322)
用一个三元组(x,y,lie)记录状态(x,y)为方块躺着的时候左或上的坐标以及立着的底坐标,躺着立着分情况处理滚动方向,注意判断边界即可。
#include
#include
#include
#include
#include
using namespace std;
struct node
{
int x,y,lie;
//lie=0, 正立x,y在正立点
//lie=1,横向x,y在左点
//lie=2,竖向x,y在上点
}st,ed;
int n,m;
char map[510][510];
bool limit(int x,int y,int lie)
{
if(x<1 || x>n|| y<1 || y>m || map[x][y]=='#') return false;
if(lie==0 && map[x][y]=='E') return false;
if(lie==1 && map[x][y+1]=='#') return false;
if(lie==2 && map[x+1][y]=='#') return false;
return 1;
}
const int ddx[]={0,0,1,-1};
const int ddy[]={1,-1,0,0};
void pre_st()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(map[i][j]=='X')
{
for(int k=0;k<4;k++)
{
int tx=i+ddx[k],ty=j+ddy[k];
if(i<1 || i>n|| j<1 || j>m || map[tx][ty]!='X')
continue;
st.x=min(i,tx),st.y=min(j,ty);
if(k<2) st.lie=1;//横向
else st.lie=2;//纵向
map[i][j]=map[tx][ty]='.';//起点是可以走的
break;
}
if(map[i][j]=='X')//没变,正立
{
st.x=i,st.y=j,st.lie=0;
map[i][j]='.';
}
}
else if(map[i][j]=='O')
{
ed.x=i,ed.y=j;
ed.lie=0;
}
}
}
//dx[i][j]:lie=i时往j方向移动的x变化情况
const int dx[3][4]={{0,0,-2,1},{1,-1,0,0},{0,0,-1,2}};
const int dy[3][4]={{-2,1,0,0},{0,0,-1,2},{1,-1,0,0}};
const int dk[3][4]={{1,1,2,2}, {1,1,0,0}, {2,2,0,0}};
int d[510][510][3];
queue q;
int bfs()
{
memset(d,-1,sizeof(d)); d[st.x][st.y][st.lie]=0;
while(q.size()) q.pop();
q.push(st);
while(q.size())
{
node u=q.front(); q.pop();
int ulie=u.lie;
for(int k=0;k<4;k++)
{
node v;
v.x=u.x+dx[ulie][k],v.y=u.y+dy[ulie][k],v.lie=dk[ulie][k];
if(!limit(v.x,v.y,v.lie))
continue;
if(d[v.x][v.y][v.lie]==-1)
{
d[v.x][v.y][v.lie]=d[u.x][u.y][u.lie]+1;
q.push(v);
if(v.x==ed.x && v.y==ed.y && v.lie==ed.lie) return d[v.x][v.y][v.lie];
}
}
}
return -1;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n&&m)
{
for(int i=1;i<=n;i++)
scanf("%s",map[i]+1);
pre_st();
int ans=bfs();
if(ans!=-1) printf("%d\n",ans);
else printf("Impossible\n");
}
return 0;
}
【例题】矩阵距离(BZOJ2252)
把所有的1插进队列中一起广搜,每个0第一次被1找到的距离就是B数组的值。
#include
#include
#include
#include
#include
using namespace std;
struct node{int x,y;};
queue q;
int n,m;
int a[1010][1010],d[1010][1010];
char ss[1010];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
int main()
{
scanf("%d%d",&n,&m);
memset(d,-1,sizeof(d));
for(int i=1;i<=n;i++)
{
scanf("%s",ss+1);
for(int j=1;j<=m;j++)
{
a[i][j]=ss[j]-'0';
if(a[i][j]==1)
{
node p; p.x=i,p.y=j;
q.push(p);
d[i][j]=0;
}
}
}
while(q.size())
{
node u=q.front(); q.pop();
for(int k=0;k<4;k++)
{
node v;
v.x=u.x+dx[k],v.y=u.y+dy[k];
if(v.x<1 || v.x>n || v.y<1 || v.y>m ) continue;
if(d[v.x][v.y]==-1)
{
d[v.x][v.y]=d[u.x][u.y]+1;
q.push(v);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;jprintf("%d ",d[i][j]);
}
printf("%d\n",d[i][m]);
}
return 0;
}
【例题】Pushing Boxes(poj1475)
BFS套BFS,箱子要往上走那么人一定要走到箱子的下端,那么我们在bfs箱子的同时bfs人能否走到推箱子的位置即可。
//WAcode
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m;
char map[50][50];
int Px,Py,Tx,Ty,Bx,By;
struct state
{
int bx,by,px,py;
string str;
};
struct node
{
int x,y;
string str;
};
const int dx[]={1, -1, 0, 0};
const int dy[]={0, 0, 1, -1};
const char Ps[]={'s', 'n', 'e', 'w'};
const char Bs[]={'S', 'N', 'E', 'W'};
bool visB[50][50],visP[50][50];
bool limit(int x,int y){if(x<1 || x>n || y<1 || y>m || map[x][y]=='#') return false; else return true;}
node pers;
bool bfs2(int stx,int sty,int edx,int edy,int boxx,int boxy)
{
node st; st.x=stx,st.y=sty; st.str="";
memset(visP,false,sizeof(visP));
visP[stx][sty]=true; visP[boxx][boxy]=true;
queue p;
while(p.size()) p.pop();
p.push(st);
while(p.size())
{
node u=p.front(); p.pop();
if(u.x==edx && u.y==edy)
{
pers=u;
return true;
}
for(int k=0;k<4;k++)
{
int nxtx=u.x+dx[k],nxty=u.y+dy[k];
if(!limit(nxtx,nxty) || visP[nxtx][nxty]) continue;
visP[nxtx][nxty]=true;
node v;
v.x=nxtx,v.y=nxty;
v.str=u.str+Ps[k];
p.push(v);
}
}
return false;
}
state ans;
bool bfs()
{
state st;
st.px=Px,st.py=Py; st.bx=Bx,st.by=By;
st.str="";
memset(visB,false,sizeof(visB)); visB[Bx][By]=true;
queue q;
while(q.size()) q.pop();
q.push(st);
while(q.size())
{
state u=q.front(); q.pop();
for(int k=0;k<4;k++)
{
int prex=u.bx-dx[k],prey=u.by-dy[k];
int nxtx=u.bx+dx[k],nxty=u.by+dy[k];
if(!limit(nxtx,nxty) || !limit(prex,prey) || visB[nxtx][nxty] ) continue;
if(bfs2(u.px,u.py,prex,prey,u.bx,u.by))
{
visB[nxtx][nxty]=true;
state v;
v.px=u.bx,v.py=u.by;
v.bx=nxtx,v.by=nxty;
v.str=u.str+pers.str+Bs[k];
if(v.bx==Tx && v.by==Ty)
{
ans=v;
return true;
}
q.push(v);
}
}
}
return false;
}
int main()
{
int ti=0;
while(scanf("%d%d",&n,&m)&&n&&m)
{
for(int i=1;i<=n;i++)
{
scanf("%s",map[i]+1);
for(int j=1;j<=m;j++)
{
if(map[i][j]=='S')
Px=i,Py=j;
if(map[i][j]=='B')
Bx=i,By=j;
if(map[i][j]=='T')
Tx=i,Ty=j;
}
}
printf("Maze #%d\n",++ti);
if(bfs())
cout<else
puts("Impossible.\n");
puts("");
}
}
【例题】电路维修
对于一格的电线有两种连接方式,对于左上角到右下角和左下角到右上角的贡献分别是0和1,建边之后广搜即可。
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=260000;
int INF;
struct node
{
int x,y,d,next;
}a[N*4]; int len,last[N];
int R,C;
void ins(int x,int y,int d)
{
a[++len].x=x;a[len].y=y;a[len].d=d;a[len].next=last[x];last[x]=len;
a[++len].x=y;a[len].y=x;a[len].d=d;a[len].next=last[y];last[y]=len;
}
char map[510][510];
int get(int x,int y){return (x-1)*(C+1)+y;}
int d[N]; bool v[N];
struct dij
{
int id;
friend bool operator <(dij a,dij b)
{
return d[a.id]>d[b.id];
}
};
priority_queue q;
int dijkstra(int S,int T)
{
while(q.size()) q.pop();
dij st,ed; st.id=S; ed.id=T;
memset(d,63,sizeof(d)); d[st.id]=0; INF=d[2];
memset(v,false,sizeof(v)); v[st.id]=true;
q.push(st);
while(q.size())
{
dij u=q.top(); q.pop();
int x=u.id;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(d[y]>d[x]+a[k].d)
{
d[y]=d[x]+a[k].d;
if(!v[y])
{
v[y]=true;
dij v; v.id=y;
q.push(v);
}
}
}
v[x]=false;
}
return d[ed.id];
}
int main()
{
// freopen("input","r",stdin);
// freopen("output","w",stdout);
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&R,&C);
len=0; memset(last,0,sizeof(last));
for(int i=1;i<=R;i++)
{
scanf("%s",map[i]+1);
for(int j=1;j<=C;j++)
{
if(map[i][j]=='/')
{
ins(get(i+1,j),get(i,j+1),0);
ins(get(i,j),get(i+1,j+1),1);
}
else
{
ins(get(i+1,j),get(i,j+1),1);
ins(get(i,j),get(i+1,j+1),0);
}
}
}
int ans=dijkstra(get(1,1),get(R+1,C+1));
if(ans==INF) puts("NO SOLUTION");
else printf("%d\n",ans);
}
return 0;
}
【例题】NightmareⅡ(HDU3085)
开两个队列,记录时间来判断是否被鬼覆盖,男孩可以走三次所以先bfs三次,同理女孩bfs一次。注意在bfs判断的时候不要让队列里面的走超过一层。
还有就是因为两人可以站着不动来等待,所以只需要在鬼覆盖不到的范围内走到对方曾经到达过的状态即可。
#include
#include
#include
#include
#include
#include
using namespace std;
const int M=810;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int n,m;
bool vis[2][M][M];
char map[M][M];
int step;
struct node
{
int x,y;
}b,g,z[2];
bool check(node p)
{
if(p.x<1 || p.x>n || p.y<1 || p.y>m) return false;
if(map[p.x][p.y]=='X') return false;
for(int i=0;i<=1;i++)
if(abs(p.x-z[i].x)+abs(p.y-z[i].y)<=2*step)
return false;
return true;
}
queue q[2];
bool bfs(int flag)
{
int sum=q[flag].size();
while(sum--)
{
node u=q[flag].front(); q[flag].pop();
if(!check(u)) continue;
for(int k=0;k<4;k++)
{
node v;
v.x=u.x+dx[k]; v.y=u.y+dy[k];
if(!check(v)) continue;
if(!vis[flag][v.x][v.y])
{
vis[flag][v.x][v.y]=1;
if(vis[flag^1][v.x][v.y]) return true;
q[flag].push(v);
}
}
}
return false;
}
int main()
{
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int cnt=0;
for(int i=1;i<=n;i++)
{
scanf("%s",map[i]+1);
for(int j=1;j<=m;j++)
{
if(map[i][j]=='M')
b.x=i,b.y=j;
else if(map[i][j]=='G')
g.x=i,g.y=j;
else if(map[i][j]=='Z')
z[cnt].x=i,z[cnt++].y=j;
}
}
step=0;
memset(vis,false,sizeof(vis));
vis[0][b.x][b.y]=vis[1][g.x][g.y]=1;
while(!q[0].empty()) q[0].pop();
while(!q[1].empty()) q[1].pop();
q[0].push(b),q[1].push(g);
int ans=-1;
while( (!q[0].empty()) && (!q[1].empty()) )
{
step++;
if(bfs(0)) {ans=step;break;}
if(bfs(0)) {ans=step;break;}
if(bfs(0)) {ans=step;break;}
if(bfs(1)) {ans=step;break;}
}
printf("%d\n",ans);
}
return 0;
}