双向广搜

题目http://acm.hdu.edu.cn/showproblem.php?pid=1043 八数码

转自https://blog.csdn.net/thudaliangrx/article/details/50659007

http://blog.sina.com.cn/s/blog_8627bf080100ticx.html

一、主控函数:

void solve()

{

1. 将起始节点放入队列q1,将目的节点放入队列q2

2. 当 两个队列都未空时,作如下循环

          1) 如果队列q1里的未处理节点比q2中的少(即tail[0]-head[0] < tail[1]-head[1]),则扩展(expand())队列q1

          2) 否则扩展(expand())队列q2 (即tail[0]-head[0] >= tail[1]-head[1]时)

3. 如果队列q1未空,循环扩展(expand())q1直到为空

4. 如果队列q2未空,循环扩展(expand())q2知道为空

}

二、扩展函数:

int expand(i) //其中i为队列的编号(表示q0或者q1)

{

          取队列qi的头结点H

          对头节点H的每一个相邻节点adj,作如下循环

                1 如果adj已经在队列qi之前的某个位置出现,则抛弃节点adj

                2 如果adj在队列qi中不存在[函数 isduplicate(i)]

                      1) 将adj放入队列qi

                      2)    如果adj 在队列(q(1-i)),也就是另外一个队列中出现[函数 isintersect()]

                                      输出 找到路径 

}

三、判断新节点是否在同一个队列中重复的函数

int isduplicate(i, j) //i为队列编号,j为当前节点在队列中的指针

{

            遍历队列,判断是否存在【线性遍历的时间复杂度为O(N),如果用HashTable优化,时间复杂度可以降到O(1)]

}

四、判断当前扩展出的节点是否在另外一个队列出现,也就是判断相交的函数:

int isintersect(i,j) //i为队列编号,j为当前节点在队列中的指针

{

          遍历队列,判断是否存在【线性遍历的时间复杂度为O(N),如果用HashTable优化,时间复杂度可以降到O(1)]

}

 

      以上为双向广度优先搜索算法的基本思路,下面给出使用上面的算法框架编写的八数码问题的代码:

问题描述:

给定 3 X 3 的矩阵如下:
2     3     4

1     5      x

7     6     8

程序每次可以交换"x"和它上下左右的数字,

经过多次移动后得到如下状态:

1      2     3

4      5     6

7      8      x

输出在最少移动步数的情况下的移动路径[每次移动的方向上下左右依次表示为'u', 'd', 'l', 'r']

 

例如:

如果过输入:【将矩阵放到一行输出】

 2  3  4 1  5  x  7  6  8


 


模板

void TBFS()

{

       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 &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);  // 入队

                       }

             }             

}                     

 

八数码 (转)

#include

#include

using namespace std;

 

#define N 10

#define MAX 365000

 

char visited[MAX];

int father1[MAX];  // 保存正向搜索当前状态的父亲状态结点

int father2[MAX];  // 保存反向搜索当前状态的父亲状态结点

int move1[MAX];    // 正向搜索的方向保存

int move2[MAX];   //  反向搜索的方向保存

 

struct Status   // 结构

{

       char eight[N];  // 八数码状态

       int space;     // x 位置

       int state;    // hash值,用于状态保存与判重 

};

 

queue Q1;  // 正向队列

queue Q2;  // 反向队列

 

Status s,s1,s2,t;

 

bool found;  // 搜索成功标记

 

int state;   // 正反搜索的相交状态

 

int factory[]={1,1,2,6,24,120,720,5040,40320,362880};  // 0..n的阶乘

 

int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

 

int Gethash(char eight[])  // 康托展开(获取状态,用于判重) //得到该序列在全排列的次序,就是得到的该序列的hash值

{

       int k=0;

       for(int i=0;i<9;i++)

       {

              int t=0;

              for(int j=i+1;j<9;j++)

                     if(eight[j]int(eight[i]))

                            num++;

              }

       }
       num=num%2;  return num;}
而本题的逆序数的奇偶性的判断是至关重要的:
如果x在同一行上面移动那么1~8的逆序数不变 (注意x是9 所以1~8 逆序数不变)
如果x在同一列上面移动(向上移动一个则移动前后相差2个,以此类推),每次逆序数增加偶数个或者减少偶数个
因为目标结点的状态的逆序数为0,为偶数,所以每次访问到的状态的逆序数也必须为偶数,保持奇偶性性质,否则就不必保存该状态。
void BFS_expand(queue &Q,bool flag)  // 单向广度搜索

{

       int k,x,y;

       s=Q.front();

       Q.pop();

       k=s.space;

       x=k/3;

       y=k%3;

       for(int i=0;i<4;i++)

       {

              int xx=x+dir[i][0];

              int yy=y+dir[i][1];

              if(xx>=0 && xx<=2 && yy>=0 && yy<=2)

              {

                     t=s;

                     t.space=xx*3+yy;   // 计算x位置

                     swap(t.eight[k],t.eight[t.space]);  // 交换两个数位置

                     t.state=Gethash(t.eight);

                     if(flag)  // 在正向队列中判断

                     {

                            if(visited[t.state]!=1 && ReverseOrder(t.eight)==0)  // 未在正向队列出现过并且满足奇偶性

                            {

                                   move1[t.state]=i;  // 保存正向搜索的方向

                                   father1[t.state]=s.state; // 保存正向搜索当前状态的父亲状态结点

                                   if(visited[t.state]==2)   //  当前状态在反向队列中出现过

                                   {

                                          state=t.state;  // 保存正反搜索中相撞的状态(及相交点)

                                          found=true;    // 搜索成功

                                          return;

                                   }

                                   visited[t.state]=1;   // 标记为在正向队列中

                                   Q.push(t);  // 入队

                            }

                     }

                     else  // 在反向队列中判断

                     {

                            if(visited[t.state]!=2 && ReverseOrder(t.eight)==0)   // 未在反向队列出现过并且满足奇偶性

                            {

                                   move2[t.state]=i;  // 保存反向搜索的方向

                                   father2[t.state]=s.state; // 保存反向搜索当前状态的父亲状态结点

                                   if(visited[t.state]==1)  //  当前状态在正向队列中出现过

                                   {

                                          state=t.state;  // 保存正反搜索中相撞的状态(及相交点)

                                          found=true;   // 搜索成功

                                          return;

                                   }

                                   visited[t.state]=2;  // 标记为在反向队列中

                                   Q.push(t);   // 入队

                            }

                     }

              }

       }

       return ;

}

 

void TBFS()            // 双向搜索

{

       memset(visited,0,sizeof(visited));

       while(!Q1.empty())

              Q1.pop();

       while(!Q2.empty())

              Q2.pop();

       visited[s1.state]=1;   // 初始状态

       father1[s1.state]=-1;

       visited[s2.state]=2;   // 目标状态

       father2[s2.state]=-1;

       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 PrintPath1(int father[],int move[])   // 从相交状态向初始状态寻找路径

{

       int n,u;

       char path[1000];

       n=1;

       path[0]=move[state];

       u=father[state];

       while(father[u]!=-1)

       {

              path[n]=move[u];

              n++;

              u=father[u];

       }

       for(int i=n-1;i>=0;--i)

       {       

              if(path[i] == 0)           

                     printf("u");       

              else if(path[i] == 1)           

                     printf("d");       

              else if(path[i] == 2)           

                     printf("l");       

              else           

                     printf("r");   

       }

}

 

void PrintPath2(int father[],int move[])   // 从相交状态向目标状态寻找路径

{

       int n,u;

       char path[1000];

       n=1;

       path[0]=move[state];

       u=father[state];

       while(father[u]!=-1)

       {

              path[n]=move[u];

              n++;

              u=father[u];

       }

       for(int i=0;i<=n-1;i++)

       {       

              if(path[i] == 0)           

                     printf("d");       

              else if(path[i] == 1)           

                     printf("u");       

              else if(path[i] == 2)           

                     printf("r");       

              else           

                     printf("l");   

       }

}

 

int main()

{

       int i;

       char c;   

       while(scanf(" %c",&c)!=EOF)

       {

              if(c=='x')

              {

                     s1.eight[0]=9;

                     s1.space=0;

              }

              else

                     s1.eight[0]=c-'0';

              for(i=1;i<9;i++)

              {

                     scanf(" %c",&c);

                     if(c=='x')

                     {

                            s1.eight[i]=9;

                            s1.space=i;

                     }

                     else

                            s1.eight[i]=c-'0';

              }

              s1.state=Gethash(s1.eight);

              for(int i=0;i<9;i++)

                     s2.eight[i]=i+1;

              s2.space=8;

              s2.state=Gethash(s2.eight);

              if(ReverseOrder(s1.eight)==1)

              {

                     cout<<"unsolvable"<


转载于:https://www.cnblogs.com/LandingGuy/p/9280243.html

你可能感兴趣的:(双向广搜)