八数码(双向广搜)POJ - 1077

八数码

The 15-puzzle has been around for over 100 years; even if you don’t know it by that name, you’ve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let’s call the missing tile ‘x’; the object of the puzzle is to arrange the tiles so that they are ordered as:

 1  2  3  4 
 
 5  6  7  8 

 9 10 11 12 

13 14 15  x    

where the only legal operation is to exchange ‘x’ with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:

 1  2  3  4    1  2  3  4    1  2  3  4    1  2  3  4 

 5  6  7  8    5  6  7  8    5  6  7  8    5  6  7  8 

 9  x 10 12    9 10  x 12    9 10 11 12    9 10 11 12 

13 14 11 15   13 14 11 15   13 14  x 15   13 14 15  x     
            r->           d->           r->  

The letters in the previous row indicate which neighbor of the ‘x’ tile is swapped with the ‘x’ tile at each step; legal values are ‘r’,‘l’,‘u’ and ‘d’, for right, left, up, and down, respectively.

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing ‘x’ tile, of course).

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three arrangement.

Input

You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus ‘x’. For example, this puzzle

 1  2  3 
 x  4  6 
 7  5  8 

is described by this list:
1 2 3 x 4 6 7 5 8

Output

You will print to standard output either the word ``unsolvable’’, if the puzzle has no solution, or a string consisting entirely of the letters ‘r’, ‘l’, ‘u’ and ‘d’ that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

Sample Input

2  3  4  1  5  x  7  6  8

Sample Output

ullddrurdllurdruldr

思路

可以将二维的八数码数字看成一维的不同数字的排列,而x就把它看成数字0就可以了,然后这题用双向广搜解的话就开两个队列,一个正向搜索,另一个反向搜索,如果两者相遇就说明有解。

  • 构造节点
    这里的节点可以利用结构体来构造,而每个节点都应有两个成员,一个用来存放数字的排列,另一个来存放操作’u’、‘d’、‘l’、‘r’.
  • 标记
    这题的数据是9位数的排列,可以采用“康托展开”来进行判重标记。这里的相遇可以用标记数组来表示: 正向搜索过的节点被标记为1,而反向搜索过的节点就标记为2
    注意:在找到解输出答案时,对于反向队列里节点的操作,要逆向输出并且如果是’r’就要输出’l’,其它的操作也一样要输出相反的方向,因为反向队列是按着反向来搜索的
    因为是用双向广搜写的,所以导致代码量较多,细节也较多,因此要养成在代码里注释的习惯,以便debug时用上
#include<iostream>
#include<queue>
#include<string>
#include<algorithm>
#include<cstdio>
using namespace std;
struct node{
 string data;
 string step;
}start,ans;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; //上下左右 
int vis[362885]={0};
long c[]={40320,5040,720,120,24,6,2,1,1};//康托数列 
char oper(int i);
long cantor(string num);
void dbfs();

int main()
{
 char t;
 start.data="";
 for(int i=0;i<9;i++)
 {
  cin>>t;
  if(t=='x')start.data+='0';
  else start.data+=t;
 }
 ans.data="123456780";
 dbfs();
 return 0;
}

char oper(int i)
{
 if(i==0)return 'u';
 if(i==1)return 'd';
 if(i==2)return 'l';
 return 'r';
}

long cantor(string num)
{
 long result=0;
 for(int i=0;i<9;i++)
 {
  int cnt=0;
  char temp=num[i];
  for(int j=i+1;j<9;j++)
  {
   if(temp>num[j])cnt++;
  }
  result+=cnt*c[i];
 }
 //cout<<num<<endl<<result<<endl;
 return result+1;
}

char change(char x)
{
 if(x=='r')return 'l';
 if(x=='l')return 'r';
 if(x=='u')return 'd';
 return 'u';
}

void dbfs()
{
 queue<node>d,f;//正向队列d,反向队列f
 start.step="";
 ans.step="";
 vis[cantor(start.data)]=1;
 vis[cantor(ans.data)]=2;
 d.push(start);
 f.push(ans);
    while(!d.empty() || !f.empty())
 {
     if(!d.empty())//正向队列 
  {
   node a;
   a=d.front();   
   d.pop();
   int z1;
   for(z1=0;z1<a.data.size();z1++)
   {
    if(a.data[z1]=='0')break;//找出0的下标 
   }
   int x,y;
   x=z1/3;
   y=z1%3;//一维转二维
   //printf("(%d,%d)\n",x,y);
   int nx,ny;
   for(int i=0;i<4;i++)
   {
    node t=a;
    nx=x+dir[i][0];
    ny=y+dir[i][1];
    if(nx>=0&&nx<3&&ny>=0&&ny<3){
     int nz=nx*3+ny;//二维转一维
     //printf("(%d,%d)\n",nx,ny);
     //cout<<nz<<endl;
     swap(t.data[nz],t.data[z1]);
     int th=cantor(t.data);
     //cin.get();    
     if(vis[th]!=1){
      t.step+=oper(i);
    if(vis[th]==2){
    for(int i=0;i<t.step.size();i++)
    {
     cout<<t.step[i];
    }
    while(f.front().data!=t.data&&!f.empty()){
     f.pop();
    }
    if(!f.empty()){
    node last=f.front();
    for(int i=last.step.size()-1;i>=0;i--)
    {
     char o=change(last.step[i]);
     cout<<o;
    }
    cout<<endl;
         }
    return;
         }
      vis[th]=1;      
      d.push(t);
           }
           }
   }
  }
     if(!f.empty())//反向队列 
  {
   node b=f.front();
   f.pop();
   int z2;
   for(z2=0;z2<b.data.size();z2++)
   {
    if(b.data[z2]=='0')break;//找出0的下标 
   }
   int x,y;
   x=z2/3;
   y=z2%3;//一维转二维
   int nx,ny;
   for(int i=0;i<4;i++)
   {
    node t=b;
    nx=x+dir[i][0];
    ny=y+dir[i][1];
    if(nx>=0&&nx<3&&ny>=0&&ny<3){
    int nz=nx*3+ny;//二维转一维 
    swap(t.data[nz],t.data[z2]);
    int th=cantor(t.data);
    if(vis[th]!=2){
        t.step+=oper(i);
        if(vis[th]==1){
    while(d.front().data!=t.data){
     d.pop();
    }
    node last2=d.front();
    for(int i=0;i<last2.step.size();i++)
    {
     cout<<last2.step[i];
    }
    for(int i=t.step.size()-1;i>=0;i--)
    {
     char o=change(t.step[i]);
     cout<<o;
    }
    cout<<endl;
    return;
       }
               vis[th]=2;
      f.push(t);
     }
           }
   }
  }
 }
 cout<<"unsolvable"<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值