八数码问题

一、问题描述



在一个3*3的方格盘上,放有八个数码,剩下第九个为空,每一空格其上下左右的数码可移至空格。问题给定初始位置和目标位置,要求通过一系列的数码移动,将初始位置转化为目标位置。本文全部目标状态都为(1, 2, 3, 4, 5, 6, 7, 8, 0)。



例如:

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

x 2 5    4 2 5    4 2 5    4 x 5    4 5 x    4 5 6

7 8 6 -> 7 8 6 -> 7 8 6 -> 7 8 6 -> 7 8 6 -> 7 8 x




二、问题分析



1、状态表示



把一个状态,自左至右,自上而下地用一个长度为9的一维数组表示,其中空格用数字0表示。



例如:

1 2 3

4 5 x

7 8 6  可以表示为:(1, 2, 3, 4, 5, 0, 7, 8, 6)



规定四个算符,(u, d, l, r)分别对应于空格的四种移动方向。

 

 

代码有些缺点,执行迭代步数有限,希望大家能有更好的算法

下面是代码:

 

 

#include <iostream>
#include <vector>

using namespace std;

//声明变量

class EiNum;

#define MAX 20 //20为最大的迭代深度

int depth; //当前树的最大深度

EiNum* root[MAX+1];//重复当前的路径

 

 


//交换两个数


void Swap(int &a,int &b)

{
int temp;

temp = a;

a=b;

b=temp;


}


class EiNum
{
private:

int child;//可以有几个子节点

int finish_child;//已经生了几个子节点

int forbbin;//移动的方向不能父节点相同

int direction[4];//分别表示空格可以向左、右、上、下四个方向移动

int depth;//当前的深度

int x,y;//保存空格的位置

 

public:
  int a[3][3];//当前八数码状态

public:
EiNum();//construction

int Compare(EiNum *s);//比较两个状态,相同返回,不相同不返回

void move();//空格可以移动几个方向

bool OneChild();//生成一个子节点

bool hasChild();//检查是否还有子节点

int reverse(int forbbin);//forbbin的反方向

bool equal(EiNum * middle ,int n);//比较节点是否相同


};

 

EiNum :: EiNum()
{
child =0;

finish_child = 0;

depth = 0;

forbbin = 10;

//x ,y初始化为无效值


x=100000;
y=100000;


int i,j;

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

  direction[i] = 0;

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

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

   a[i][j] = 0;

 

}

//比较两个状态,相同返回,不相同返回

int EiNum::Compare(EiNum *s)

{

int i,j;

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

  for (j = 0 ; j<3 ;j++)
  {
   if (a[i][j]==s->a[i][j])
    continue;
   else return 0;//不相同
  }

  return 1;//相同

 


}

//比较当前节点是否与父节点和祖先节点相同

bool EiNum::equal(EiNum *s ,int n)
{
int i,j,m;

int flag = 0;

for (m=0; m<n;m++)
{
  flag = 0;

  for(j=0;j<3;j++)
  {
   if (root

­->a[i][j] == s->a[i][j])
    continue;

   else flag = 1;
  }

  if ( i == 3 && j == 3 )

  return false;//相同

}


return true;//不相同


}


//空格可以移动几个方向

void EiNum::move()
{
int i,j;

child = 0;

finish_child = 0;

//初始化


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

  direction[i] = 0;

//找到空格的位置

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

  {

   if (a[i][j]==0) break;
  }

  if (a[i][j]==0&&j<3) break;


}


x=i,y=j;

//根据空格的位置不同,获得可以生成节点的数目和走向的方向


if (x==0 && y==0)
{
  child=2;

  direction[1] = 1;
  direction[3] = 1;
}

else if (x == 0 && y == 1)
{
  child = 3;

  direction[0] = 1;

     direction[1] = 1;

  direction[3] = 1;
}


else if (x==0 && y==2)
{
  child = 2;

  direction[0] =1;

  direction[3] = 1;

}

else if (x==1 && y==0)
{
  child = 3;

  direction[1] = 1;

  direction[2] = 1;

  direction[3] = 1;
}


else if (x==1 && y==1)

{
  child =4;

  direction[0] = 1;

  direction[1] = 1;

  direction[2] = 1;

  direction[3] = 1;

}

else if (x==1 && y==2)
{
  child = 3 ;

  direction[0] = 1;

  direction[2] = 1;

  direction[3] = 1;
}


else if (x == 2 && y == 0)
{
  child = 2 ;

  direction[1] = 1;

  direction[2] = 1;
}


else if (x==2 && y==1)
{
  child = 3 ;

  direction[0] = 1;

  direction[1] = 1;

  direction[2] = 1;

}


else if (x==2 && y==2)
{
  child =2;

  direction[0] = 1;

  direction[2] = 1;
}

else ;

// 如果和其父节点的移动方向相反也即又重新移动到之前的位置,则这个节点不能生成


if ( reverse(forbbin)<5 && direction[reverse(forbbin)] == 1 )

{
  child--;

  direction[reverse(forbbin)] =0;


}

 

 

 

}

//forbbin的反方向也即父节点的反方向

int EiNum::reverse(int forbbin)
{
switch(forbbin)
{
case 0:

  return 1;
case 1:

  return 0;

case 2:

  return 3;

case 3:

  return 2;

default:

  return 5;
}
}

 


//ch为生成的子节点


EiNum *ch ;

//生成一个子节点

bool EiNum::OneChild()//如果有孩子节点返回ture,否则false
{
if (child == finish_child )
  return false ;

else
{
  int i;

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

  {
   if (direction[i] == i)
   {
    direction[i] = 0;

    ch = new EiNum;

    ch->forbbin = i;//子节点不能相反的方向

    int m , n;

    for (m=0;m<3;m++)

     for (n=0;n<3;n++)

      ch->a

­[n] = a

­[n];


     switch(ch->forbbin)
     {
     case 0:

      Swap(ch->a[x][y-1],ch->a[x][y]);

      break;


     case 1:

      Swap(ch->a[x][y+1],ch->a[x][y]);

      break;

     case 2:

      Swap(ch->a[x-1][y],ch->a[x][y]);

      break;

     case 3:

      Swap(ch->a[x+1][y],ch->a[x][y]);

      break;
     }


     finish_child++;

     return true;

   }
  }
}

}


//检查是否还有节点


bool EiNum::hasChild()
{
if (child == finish_child )
  return false;

else return true ;
}


//打印出结果 ,也即从初始化状态到目标状态的路径

void printEiNum(EiNum *s)

{
int i,j;

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

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

  { cout << s->a[i][j]<<' ';

  cout <<endl;
  }


  cout<<endl;


}

 

int main ()
{
cout << "请输入八数码的初始状态,如"<< endl;

cout << "说明:表格空格"<<endl;


cout << "1 2 3 /n 4 5 6 /n 7 0 8" << endl;

cout << "初始状态如下:" <<endl;

//start为初始状态

EiNum *start = new EiNum;

int i,j;


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

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

   cin >> start->a[i][j];

  root[0] = start;

  cout << "请输入八数码的最终要达到的状态" << endl;

  cout << "1 2 3 /n 4 5 6 /n 7 0 8  " << endl;

  cout <<"终止状态如下:"<<endl;

 

  //final为目标状态

  EiNum *final = new EiNum;

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

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

    cin >> final->a[i][j];


   //比较初始化状态和目标状态是否相同

   if ( start->Compare(final) == 1)
   {
    cout << "初始化和目标状态相同!" << endl;

    return 0;

   }

 

   for (depth = 1; depth < MAX ; depth++)
   {
    cout << "当前迭代深度为:" << depth <<endl;

 

    int  flag = 0;   //标志当前是否是新生成的


    start->move(); //空格可以移动几个方向

    int i = 0; //


    //迭代加深的算法

    for(i=0;i<=depth&& i>=0;)

    {//找到目标位置

     if (flag == 0 && root[i]->Compare(final) == 1)

     {
      cout <<"目标状态已找到!!"<<endl;

      cout << "输出移动的路径" << endl;


      int j=0;

      cout <<"初始状态:"<<endl;

      printEiNum(start);

      for (j=1;j<=depth-1;j++)
      {
       cout <<"第"<<j<<"次移动"<<endl;

       printEiNum(root[j]);
      }

      cout << "输出目标状态:" << endl;

      cout << "最后一步移动:" << endl;

      printEiNum(final);

      return ;

 


     }

     //生成子节点

     if (root[i]->OneChild() == true && i < depth)
     {

      //如果和父节点重复 则生成另外的子节点

      while (root[i]->hasChild()&& ch->equal(ch,i) == false )

       root[i]->OneChild();


      //当前的深度继续增加


      i++;

      //当前的子节点加入到路径中

      root[i] = ch ;

      //当前的节点有几个子节点

      root[i]->move();

      flag = 0;

 

 


     }

     //向上回溯

     else
     {
      i--;
      flag = 1;

     }

 

 

 

    }

 


   }

   if (depth == MAX) cout << "迭代深度已到!未找到答案" <<endl;

 

 

 

 

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值