双向BFS模板

如果已经知道搜索的开始状态和结束状态, 要找一个满足某种条件的一条路径(一般是最短路径),为了避免无谓的“组合爆炸”产生,就可以采取双向广度搜索算法,也就是从开始状态和结束状态同时开始搜索,一个向前搜,一个向后找。 这样做的好处是什么? 我们不妨假设每次搜索的分支因子是r,如果最短的路径长为L的话(也就是搜了L层),那么,用一般的BFS算法(不考虑去掉重复状态),总的搜索状态数是r^L(^表示乘方运算);而如果采取双向BFS算法,那么,从前往后搜,我们只需要搜索L/2层,从后往前搜,我们也只要搜L/2层,因此,搜索状态数是2*(r^(L/2)),比普通BFS就快了很多了。 双向BFS算法的实质还是BFS,只不过两边同时开始BFS而已。还是可以利用队列来实现:可以设置两个队列,一个用于向前的BFS,另一个用于向后的BFS,利用这两个队列,同时从前、后开始层次遍历搜索树。

è¿éåå¾çæè¿°
双向广搜模版:

void BFS_bothsides()//双向BFS 
{
    if(s1.state==s2.state)//起点终点相同时要特判
    {
           //do something
           found=true;
           return;
    }
    bool found=false;
    memset(visited,0,sizeof(visited));  // 判重数组
    while(!Q1.empty())  Q1.pop();   // 正向队列
    while(!Q2.empty())  Q2.pop();  // 反向队列
    //======正向扩展的状态标记为1,反向扩展标记为2
    visited[s1.state]=1;   // 初始状态标记为1
    visited[s2.state]=2;   // 结束状态标记为2
    Q1.push(s1);  // 初始状态入正向队列
    Q2.push(s2);  // 结束状态入反向队列
    while(!Q1.empty() || !Q2.empty())
    {
           if(!Q1.empty())
                  BFS_expand(Q1,true);  // 在正向队列中搜索
           if(found)  // 搜索结束 
                  return ;
          if(!Q2.empty())
                  BFS_expand(Q2,false);  // 在反向队列中搜索
           if(found) // 搜索结束
                  return ;
    }
}
void BFS_expand(queue<Status> &Q,bool flag)
{  
 	s=Q.front();  // 从队列中得到头结点s
 	Q.pop()
 	for( 每个s 的子节点 t )
	{
        t.state=Gethash(t.temp);  // 获取子节点的状态
        if(flag)   // 在正向队列中判断
        {
           	if(visited[t.state]!=1)// 没在正向队列出现过
            {
                if(visited[t.state]==2)  // 该状态在反向队列中出现过
              	{
                    各种操作;
                    found=true;
                    return;
                }
                visited[t.state]=1;   // 标记为在在正向队列中
                Q.push(t);  // 入队
           	}
        }
        else    // 在正向队列中判断
        {
            if (visited[t.state]!=2) // 没在反向队列出现过
         	{
                if(visited[t.state]==1)  // 该状态在正向向队列中出现过
                {
                    各种操作;
                    found=true;
                    return;
                }
                visited[t.state]=2;  // 标记为在反向队列中
                Q.push(t);  // 入队
            }
        }
    }
}

下面上一道例题熟悉下代码:

杭电-open the lock

题意:
解开密码锁,从初始状态到开锁状态问最少做几次操作。操作可以是上下转动(上下加减1,特别的:9 + 1 = 1, 1 - 0 = 9 )和交换两个相邻位(最左面的和最右面的不相邻)。一共8+3=11种状态扩展

标程:

#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int MAXN=10010;
int vis[MAXN];
int step[MAXN];
struct Node
{
    int num[5];
    int status;
}st,ed;
int flag;
int minStep;
int toInt(int num[])
{
    return num[0]*1000+num[1]*100+num[2]*10+num[3];
}
void toArray(int num,int arr[])
{
    arr[0]=num/1000;
    arr[1]=(num%1000)/100;
    arr[2]=(num%100)/10;
    arr[3]=num%10;
}
void BFS_expand(queue<Node> & que,int choose)
{
        Node curr=que.front(),next;
        que.pop();
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<2;j++)
            {
                next=curr;
                if(j==0)
                {
                    next.num[i]=curr.num[i]+1;
                    if(next.num[i]==10) next.num[i]=1;
                }
                else
                {
                    next.num[i]=curr.num[i]-1;
                    if(next.num[i]==0) next.num[i]=9;
                }
                next.status=toInt(next.num);
                int dis=step[curr.status]+1;
                if(choose==0)
                {
                    if(vis[next.status]!=1)
                    {
                        if(vis[next.status]==2)
                        {
                            flag=true;
                            minStep=step[next.status]+dis;
                            return;
                        }
                        step[next.status]=dis;
                        vis[next.status]=1;
                        que.push(next);
                    }
                }
                else
                {
                    if(vis[next.status]!=2)
                    {
                        if(vis[next.status]==1)
                        {
                            flag=true;
                            minStep=step[next.status]+dis;
                            return;
                        }
                        step[next.status]=dis;
                        vis[next.status]=2;
                        que.push(next);
                    }
                }
            }
        }
        for(int i=0;i<3;i++)
        {
                next=curr;
                next.num[i]=curr.num[i+1];
                next.num[i+1]=curr.num[i];
                next.status=toInt(next.num);
                int dis=step[curr.status]+1;
                if(choose==0)
                {
                    if(vis[next.status]!=1)
                    {
                        if(vis[next.status]==2)
                        {
                            minStep=dis+step[next.status];
                            flag=true;
                            return;
                        }
                        vis[next.status]=1;
                        step[next.status]=dis;
                        que.push(next);
                    }
                }
                else
                {
                    if(vis[next.status]!=2)
                    {
                        if(vis[next.status]==1)
                        {
                            minStep=dis+step[next.status];
                            flag=true;
                            return;
                        }
                        vis[next.status]=2;
                        step[next.status]=dis;
                        que.push(next);
                    }
                }
        }
}
void TBFS()
{
    if(st.status==ed.status)//起点终点相同时要特判
    {
        minStep=0;
        return;
    }
    queue<Node> q1;
    queue<Node> q2;
    q1.push(st);
    q2.push(ed);
    memset(vis,0,sizeof(vis));
    vis[st.status]=1;
    vis[ed.status]=2;
    step[st.status]=0;
    step[ed.status]=0;
    flag=false;
    while(!q1.empty()||!q2.empty())
    {
        if(!q1.empty())
        {
            BFS_expand(q1,0);
        }
        if(flag) return;
        if(!q2.empty())
        {
            BFS_expand(q2,1);
        }
        if(flag) return;
    }
}
int main()
{
    int t,s1,s2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&s1,&s2);
        toArray(s1,st.num);
        st.status=s1;
        toArray(s2,ed.num);
        ed.status=s2;
        TBFS();
        printf("%d\n",minStep);
    }
    return 0;
}

代码部分转自:https://www.jianshu.com/p/ea5ca82e6bc6

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值