八数码宽度优先算法
2015-11-29 17:52
399 查看
相关代码要感谢网上的一些大神给予的分享
struct Map
{
int cell
;//数码数组
Direction BelockDirec;//操作符方向
struct Map * Parent;//父节点
};
void PrintMap(struct Map *map)
{
cout<<"*************************************************"<<endl;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
cout<<map->cell[i][j]<<" ";
}
cout<<endl;
}
cout<<"*************************************************"<<endl;
}
struct Map * MoveMap(struct Map * map,Direction Direct,bool CreateNewMap)
{
struct Map * NewMap;
//获取空闲格位置i,j
int i,j;
for(i = 0; i < N; i++)
{
bool HasGetBlankCell = false;
for(j = 0; j < N; j++)
{
if(map->cell[i][j] == 0)
{
HasGetBlankCell = true;
break;
}
}
if(HasGetBlankCell)
break;
}
//移动数字,t_i,t_j为移动后0的坐标
int t_i = i,t_j = j;
bool AbleMove = true;
switch(Direct)
{
case Down:
t_i++;
if(t_i >= N)
AbleMove=false;
break;
case Up:
t_i--;
if(t_i < 0)
AbleMove=false;
break;
case Left:
t_j--;
if(t_j < 0)
AbleMove=false;
break;
case Right:
t_j++;
if(t_j >= N)
AbleMove=false;
break;
};
if(!AbleMove)//不可以移动则返回原节点
{
return map;
}
if(CreateNewMap)
{
NewMap = new Map();
for(int x = 0; x < N; x++)
for(int y = 0; y < N; y++)
NewMap->cell[x][y] = map->cell[x][y];
}
else
NewMap = map;
/*移动即交换0与相应数字的位置*/
NewMap->cell[i][j] = NewMap->cell[t_i][t_j];
NewMap->cell[t_i][t_j] = 0;
return NewMap;
}
struct Map * RandomMap() //随机产生八数码数组
{
int a[9];
struct Map * NewMap;
NewMap = new Map();
srand((unsigned)time(0));
for(int k = 0; k < 9; k++)
{
bool Isre = false;
a[k] = rand()%9;
for (int l = 0; l < k; l++)
{
if (a[k] == a[l])
{
Isre = true;
break;
}
}
if(Isre)
{
k = k - 1;
continue;
}
}
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
NewMap->cell[i][j] = a[i*3+j];
}
}
NewMap->Parent = NULL;
NewMap->BelockDirec = None;
return NewMap;
}
bool IsSuccess(struct Map * map,struct Map * Target)
{
bool IsSuc = true;
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
if(map->cell[i][j] != Target->cell[i][j])
{
IsSuc = false;
break;
}
}
if(!IsSuc)
break;
}
return IsSuc;
}
用来进行宽度搜索(按照以下的流程)
因为CLOSED表是只进不出的,所以这里省略了CLOSED表,用P1保存CLOSED表中的最新结点。而OPEN表是一个队列,先进先出。程序是以链表作为数据结构的,而不是数组。所以可以通过指针回溯
//把begin结点放入OPEN表中
struct Map * p1, *p2, *p=NULL;//p1为要进入closed表中的结点,p2为p1扩展的结点,p为等于结果的结点
bool IsSucc = false;
queue<struct Map *> Queue;
if(IsSuccess(begin,Target))
return begin;
Queue.push(begin);
//begin是否为目标结点
if(IsSuccess(begin,Target))
return begin;
//OPEN表是否为空表,或是否已经找到结果
while(!Queue.empty() || p == NULL);
//把第一个结点从OPEN表中移出,放入CLOSED表中(即p1中)
p1 = Queue.front(); //返回queue中的第一个元素
Queue.pop();
//扩展当前结点
for (int i = 1; i <= 4; i++)
{
Direction Direct=(Direction)i;
if(Direct == p1->BelockDirec)//跳过屏蔽方向,因为里面保存了之前前进的方向,防止回退
continue;
p2 = MoveMap(p1,Direct,true);//p2为扩展的的结点
//如果可以扩展成功(即可以移动)则设置方向和父节点指针
if(p2 != p1)//数码是否可以移动
{
p2->Parent = p1;
switch(Direct)//设置屏蔽方向,防止往回推
{
case Up:
p2->BelockDirec = Down;
break;
case Down:
p2->BelockDirec = Up;
break;
case Left:
p2->BelockDirec = Right;
break;
case Right:
p2->BelockDirec = Left;
break;
}
//判断后继结点是否为目标结点,如果不是则将后继结点放入OPEN表中并循环从OPEN表中pop,如果是则返回当前结点
if (IsSuccess(p2,Target))
{
p = p2;
return p;
}
Queue.push(p2);
n++;
如果奇偶相同说明有解,否则无解
int HasResult(struct Map *map) //返回逆序数的奇偶用来判断是否有解
{
int a=0;
char b[9];
//把map二维表变为数组
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
b[i*3+j]=map->cell[i][j];
}
for(i=0;i<9;i++)
{
for(int j=0;j<i;j++)
{
if((b[j]<b[i])&&b[j]!=0)
a++;
}
}
return a%2;
}
定义a1,a2用来记录begin和target逆序数的奇偶性用以判断是否有解
int a1,a2;
Map Target;
Map *begin,*T;
begin=new Map;
//设定目标图 [1 2 3],[8 0 4],[7 6 5]
Target.cell[0][0] = 1;
Target.cell[0][1] = 2;
Target.cell[0][2] = 3;
Target.cell[1][0] = 8;
Target.cell[1][1] = 0;
Target.cell[1][2] = 4;
Target.cell[2][0] = 7;
Target.cell[2][1] = 6;
Target.cell[2][2] = 5;
begin = RandomMap();
cout<<"目标图:"<<endl;
PrintMap(&Target);
cout<<"起始图:"<<endl;
PrintMap(begin);
a1=HasResult(&Target);
a2=HasResult(begin);
if(a1!=a2)
cout<<"无解"<<endl;
如果有解则进行BNF搜索并打印结果:
else
{
double start=clock();
cout<<"有解"<<endl;
//图搜索
T=BNF_Search(begin,&Target);
//打印
if(T != NULL)
{
//把路径倒序
Map *p=T;
stack<Map *> result_Stack1; //栈,先进后出
while(p->Parent != NULL)
{
result_Stack1.push(p);
p = p->Parent;
}
cout<<"搜索结果:"<<endl;
while(!result_Stack1.empty())
{
PrintMap(result_Stack1.top());
c++;
result_Stack1.pop();//删除
}
cout<<"\n完成!"<<endl;
cout<<"路径长度为:"<<c<<endl;
double end=clock();
cout<<"程序运行的时间为:"<<end-start<<endl;
}
}
无解:
程序思路
结构体map
用来作为一个结点的数据结构。struct Map
{
int cell
;//数码数组
Direction BelockDirec;//操作符方向
struct Map * Parent;//父节点
};
PrintMap
用来打印一个结点void PrintMap(struct Map *map)
{
cout<<"*************************************************"<<endl;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
cout<<map->cell[i][j]<<" ";
}
cout<<endl;
}
cout<<"*************************************************"<<endl;
}
MoveMap
函数用来扩展一个结点的后继结点(上、下、左、右)struct Map * MoveMap(struct Map * map,Direction Direct,bool CreateNewMap)
{
struct Map * NewMap;
//获取空闲格位置i,j
int i,j;
for(i = 0; i < N; i++)
{
bool HasGetBlankCell = false;
for(j = 0; j < N; j++)
{
if(map->cell[i][j] == 0)
{
HasGetBlankCell = true;
break;
}
}
if(HasGetBlankCell)
break;
}
//移动数字,t_i,t_j为移动后0的坐标
int t_i = i,t_j = j;
bool AbleMove = true;
switch(Direct)
{
case Down:
t_i++;
if(t_i >= N)
AbleMove=false;
break;
case Up:
t_i--;
if(t_i < 0)
AbleMove=false;
break;
case Left:
t_j--;
if(t_j < 0)
AbleMove=false;
break;
case Right:
t_j++;
if(t_j >= N)
AbleMove=false;
break;
};
if(!AbleMove)//不可以移动则返回原节点
{
return map;
}
if(CreateNewMap)
{
NewMap = new Map();
for(int x = 0; x < N; x++)
for(int y = 0; y < N; y++)
NewMap->cell[x][y] = map->cell[x][y];
}
else
NewMap = map;
/*移动即交换0与相应数字的位置*/
NewMap->cell[i][j] = NewMap->cell[t_i][t_j];
NewMap->cell[t_i][t_j] = 0;
return NewMap;
}
RandomMap
函数用来随机产生beginMapstruct Map * RandomMap() //随机产生八数码数组
{
int a[9];
struct Map * NewMap;
NewMap = new Map();
srand((unsigned)time(0));
for(int k = 0; k < 9; k++)
{
bool Isre = false;
a[k] = rand()%9;
for (int l = 0; l < k; l++)
{
if (a[k] == a[l])
{
Isre = true;
break;
}
}
if(Isre)
{
k = k - 1;
continue;
}
}
for(int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
NewMap->cell[i][j] = a[i*3+j];
}
}
NewMap->Parent = NULL;
NewMap->BelockDirec = None;
return NewMap;
}
IsSuccess
函数用来判断某节点是否为目标结点bool IsSuccess(struct Map * map,struct Map * Target)
{
bool IsSuc = true;
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
if(map->cell[i][j] != Target->cell[i][j])
{
IsSuc = false;
break;
}
}
if(!IsSuc)
break;
}
return IsSuc;
}
BNF_Search
struct Map * BNF_Search(struct Map * begin,struct Map * Target)用来进行宽度搜索(按照以下的流程)
因为CLOSED表是只进不出的,所以这里省略了CLOSED表,用P1保存CLOSED表中的最新结点。而OPEN表是一个队列,先进先出。程序是以链表作为数据结构的,而不是数组。所以可以通过指针回溯
//把begin结点放入OPEN表中
struct Map * p1, *p2, *p=NULL;//p1为要进入closed表中的结点,p2为p1扩展的结点,p为等于结果的结点
bool IsSucc = false;
queue<struct Map *> Queue;
if(IsSuccess(begin,Target))
return begin;
Queue.push(begin);
//begin是否为目标结点
if(IsSuccess(begin,Target))
return begin;
//OPEN表是否为空表,或是否已经找到结果
while(!Queue.empty() || p == NULL);
//把第一个结点从OPEN表中移出,放入CLOSED表中(即p1中)
p1 = Queue.front(); //返回queue中的第一个元素
Queue.pop();
//扩展当前结点
for (int i = 1; i <= 4; i++)
{
Direction Direct=(Direction)i;
if(Direct == p1->BelockDirec)//跳过屏蔽方向,因为里面保存了之前前进的方向,防止回退
continue;
p2 = MoveMap(p1,Direct,true);//p2为扩展的的结点
//如果可以扩展成功(即可以移动)则设置方向和父节点指针
if(p2 != p1)//数码是否可以移动
{
p2->Parent = p1;
switch(Direct)//设置屏蔽方向,防止往回推
{
case Up:
p2->BelockDirec = Down;
break;
case Down:
p2->BelockDirec = Up;
break;
case Left:
p2->BelockDirec = Right;
break;
case Right:
p2->BelockDirec = Left;
break;
}
//判断后继结点是否为目标结点,如果不是则将后继结点放入OPEN表中并循环从OPEN表中pop,如果是则返回当前结点
if (IsSuccess(p2,Target))
{
p = p2;
return p;
}
Queue.push(p2);
n++;
HasResult
函数返回一个结点逆序数的奇偶性用来判断八数码是否可以后的结果如果奇偶相同说明有解,否则无解
int HasResult(struct Map *map) //返回逆序数的奇偶用来判断是否有解
{
int a=0;
char b[9];
//把map二维表变为数组
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
b[i*3+j]=map->cell[i][j];
}
for(i=0;i<9;i++)
{
for(int j=0;j<i;j++)
{
if((b[j]<b[i])&&b[j]!=0)
a++;
}
}
return a%2;
}
Main函数
随机生成begin结点,并定义target结点定义a1,a2用来记录begin和target逆序数的奇偶性用以判断是否有解
int a1,a2;
Map Target;
Map *begin,*T;
begin=new Map;
//设定目标图 [1 2 3],[8 0 4],[7 6 5]
Target.cell[0][0] = 1;
Target.cell[0][1] = 2;
Target.cell[0][2] = 3;
Target.cell[1][0] = 8;
Target.cell[1][1] = 0;
Target.cell[1][2] = 4;
Target.cell[2][0] = 7;
Target.cell[2][1] = 6;
Target.cell[2][2] = 5;
begin = RandomMap();
cout<<"目标图:"<<endl;
PrintMap(&Target);
cout<<"起始图:"<<endl;
PrintMap(begin);
a1=HasResult(&Target);
a2=HasResult(begin);
if(a1!=a2)
cout<<"无解"<<endl;
如果有解则进行BNF搜索并打印结果:
else
{
double start=clock();
cout<<"有解"<<endl;
//图搜索
T=BNF_Search(begin,&Target);
//打印
if(T != NULL)
{
//把路径倒序
Map *p=T;
stack<Map *> result_Stack1; //栈,先进后出
while(p->Parent != NULL)
{
result_Stack1.push(p);
p = p->Parent;
}
cout<<"搜索结果:"<<endl;
while(!result_Stack1.empty())
{
PrintMap(result_Stack1.top());
c++;
result_Stack1.pop();//删除
}
cout<<"\n完成!"<<endl;
cout<<"路径长度为:"<<c<<endl;
double end=clock();
cout<<"程序运行的时间为:"<<end-start<<endl;
}
}
程序测试
有解:无解:
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性
- C++ Custom Control控件向父窗体发送对应的消息