您的位置:首页 > 其它

八数码问题

2010-05-14 15:10 169 查看
一、问题描述

在一个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

­
= a

­
;

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: