广搜与广搜变形

广搜

相较于深度搜索的一条路走到黑,广度搜索如其名是逐层搜索,一次把整一层搜完。我们用一个队列来实现,每次取出队头,对于队头状态的所有分支,把沿着分支所能达到的下一个状态(如果这个状态没被访问过或者可以形成更优的解)插入队尾,直到队列为空。

【例题】Bloxorz(poj3322)
用一个三元组(x,y,lie)记录状态(x,y)为方块躺着的时候左或上的坐标以及立着的底坐标,躺着立着分情况处理滚动方向,注意判断边界即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
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<node> 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<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node{int x,y;};
queue<node> 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;j<m;j++)
        {
            printf("%d ",d[i][j]);
        }
        printf("%d\n",d[i][m]);
    }
    return 0;

}

【例题】Pushing Boxes(poj1475)
BFS套BFS,箱子要往上走那么人一定要走到箱子的下端,那么我们在bfs箱子的同时bfs人能否走到推箱子的位置即可。

//WAcode
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
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<node> 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<state> 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<<ans.str<<endl;
        else
            puts("Impossible.\n");
        puts("");
    }
}

广搜变形

【例题】电路维修
对于一格的电线有两种连接方式,对于左上角到右下角和左下角到右上角的贡献分别是0和1,建边之后广搜即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
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<dij> 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;
}

双向队列BFS

【例题】NightmareⅡ(HDU3085)
开两个队列,记录时间来判断是否被鬼覆盖,男孩可以走三次所以先bfs三次,同理女孩bfs一次。注意在bfs判断的时候不要让队列里面的走超过一层。
还有就是因为两人可以站着不动来等待,所以只需要在鬼覆盖不到的范围内走到对方曾经到达过的状态即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
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<node> 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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值