zoukankan      html  css  js  c++  java
  • [转载]双向广搜

    原文地址:双向广搜作者:qingge

     

      Eight   

       题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043

       讲到双向广搜,那就不能不讲经典的八数码问题,有人说不做此题人生不完整 。

       所谓双向广搜,就是初始结点向目标结点和目标结点向初始结点同时扩展,直至在两个扩展方向上出现同一个结点,搜索结束。它适用的问题是,扩展结点较多,而目标结点又处在深沉,如果采用单纯的广搜解题,搜索量巨大,搜索速度慢是可想而知的,同时往往也会出现内存空间不够用的情况,这时双向广搜的作用就体现出来了。双向广搜对单纯的广搜进行了改良或改造,加入了一定的“智能因数”,使搜索能尽快接近目标结点,减少了在空间和时间上的复杂度。

       当在讲题前,不得不先给大家补充一点小知识,大家都知道搜索的题目其中难的一部分就是事物的状态,不仅多而且复杂,要怎么保存每时刻的状态,又容易进行状态判重呢,这里提到了一种好办法   ------康托展开(只能针对部分问题)

     

    康托展开

    康托展开式:

      X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!

     其中,a为整数,并且0<=ai<i(1<=i<=n)。

     

    例:

    问:1324是{1,2,3,4}排列数中第几个大的数?

    解:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

     

    好吧,先看下代码实现:

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

     

    int Gethash(char eight[])

    {

           int k=0;

           for(int i=0;i<9;i++)    // 因为它有八位数(针对八数码问题)

           {

                  int t=0;

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

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

                                t++;

                  k+=(t*factory[9-i-1]);

           }

           return k;   // 返回该数是第几大

    }

    好的,现在再来看看双向广搜模版:

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

                          

                 }             

                       

     

    好的,现在开始说说八数码问题

    其实,Eight有一个很重要的判断,那就是逆序数的判断。如果i>j,并且ai<aj,那么定义(i,j)为一个逆序对,而对于一个状态排列中所含的逆序对的个数总和就是逆序数。而本题的逆序数的奇偶性的判断是至关重要的:

    如果x在同一行上面移动那么1~8的逆序数不变

    如果x在同一列上面移动,每次逆序数增加偶数个或者减少偶数个

    因为目标结点的状态的逆序数为0,为偶数,所以每次访问到的状态的逆序数也必须为偶数,保持奇偶性性质,否则就不必保存该状态。

     

    #include<iostream>

    #include<queue>

    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<Status> Q1;  // 正向队列

    queue<Status> 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[])  // 康托展开(获取状态,用于判重)

    {

           int k=0;

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

           {

                  int t=0;

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

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

                                t++;

                  k+=(t*factory[9-i-1]);

           }

           return k;

    }

     

    int ReverseOrder(char eight[])  // 求状态的逆序数

    {

           int i,j,num=0;

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

           {

                  for(j=0;j<i;j++)

                  {

                         if(int(eight[i])==9)

                         {

                                break;

                         }

                         if(int(eight[j])==9)

                                continue;

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

                                num++;

                  }

           }

           num=num%2;

           return num;

    }

     

    void BFS_expand(queue<Status> &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"<<endl;

                         continue;

                  }

                  found=false;

                  TBFS();

                  if(found)   // 搜索成功

                  {

                         PrintPath1(father1,move1);

                         PrintPath2(father2,move2);

                  }

                  else

                         cout<<"unsolvable"<<endl;

                  cout<<endl;

           }

           return 0;

    }

  • 相关阅读:
    Spring Boot 使用 Dom4j XStream 操作 Xml
    Spring Boot 使用 JAX-WS 调用 WebService 服务
    Spring Boot 使用 CXF 调用 WebService 服务
    Spring Boot 开发 WebService 服务
    Spring Boot 中使用 HttpClient 进行 POST GET PUT DELETE
    Spring Boot Ftp Client 客户端示例支持断点续传
    Spring Boot 发送邮件
    Spring Boot 定时任务 Quartz 使用教程
    Spring Boot 缓存应用 Memcached 入门教程
    ThreadLocal,Java中特殊的线程绑定机制
  • 原文地址:https://www.cnblogs.com/csgc0131123/p/5290396.html
Copyright © 2011-2022 走看看