八数码问题
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;
}
在一个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;
}
相关文章推荐
- 路径寻找——八数码问题
- 【POJ1077】Eight 八数码问题,解题报告+思路+代码
- HDU 1043 八数码问题 A*搜索+康拓展开+逆序对判断+路径输出
- 八数码问题——A*大法好
- 八数码问题
- 启发式搜索程序设计-八数码问题
- 八数码问题
- 八数码问题(紫薯P199)
- hdu(4021)八数码的解是否存在问题
- BFS:八数码问题
- 八数码问题的可解行
- hdu 6048 Puzzle 思维(8数码问题
- hdu 6048 Puzzle 思维(8数码问题
- 【启发式搜索】八数码问题
- 数码问题合集
- HDU 1043 八数码问题的多种解法
- poj 1077 bfs+康托展开(8数码问题)
- 人工智能之八数码问题
- Poj 1077 Eight 八数码问题 (搜索)
- 八数码问题