基于A*算法的八数码问题(九宫问题)解法的程序实现(新增有无解判断)
2012-04-23 21:01
495 查看
题目叙述:九宫问题又称“八数码问题”,是说在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,从某种初始状态开始,对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态变换至目标状态。求其最少步数。
/*****************************/
/*EIGHTDIGITPROBLEM*/
/*唐国峰2012年4月23日*/
/*****************************/
//预编译命令
#include"iostream"
#include"stdlib.h"
usingnamespacestd;
//棋盘大小
#definesize3
//定义二维数组来存储数据表示某一个特定状态
typedefintstatus[size][size];
//定义状态图中的节点数据结构,即节点的状态信息等
typedefstructNode
{
statusdata;//节点所存储的状态
structNode*parent;//指向节点的父亲节点
structSpringLink*child;//指向节点的后继节点
structNode*next;//指向链表的后一个节点
intf_value;//由初始状态经由当前节点至目标状态的总耗散值
intg_value;//由初始状态经到当前节点实际耗散值
inth_value;//由当前节点到目标状态的预计耗散值
}NNode,*PNode;
//定义表述指向当前节点的扩展节点的链表
typedefstructSpringLink
{
structNode*pointData;//指向节点的指针
structSpringLink*next;//指向当前节点的其他扩展节点
}SPLink,*PSPLink;
//声明OPEN表和CLOSED表
PNodeopen;
PNodeclosed;
//计算棋盘状态的逆序数
intInverseNumber(statusa)
{
inti,j,sum=0;
intdata_chang[size*size]={0};
//将二维数组转换成一维数组,以方便求逆序数
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
data_chang[i*size+j]=a[i][j];
}
}
//计算序列中除零外的逆序数
for(i=0;i<=size*size;i++)
{
if(data_chang[i]!=0)
{
//要比较多少次,从最后一个元素开始比较
for(j=i;j>=0;j--)
{
//当后一个数比前一个数小时
if(data_chang[i]<data_chang[j])
{
sum++;
}
}
}
}
returnsum;
}
//判断是否存在解决方案
boolhasSolution(statusstartStatus,statustargetStatus)
{
intstartInverseNumber=InverseNumber(startStatus);
inttatgetInverseNumber=InverseNumber(targetStatus);
//判断初始状态和目标状态除零外的序列逆序数奇偶性,相同则可求值,不同则不可求
if((startInverseNumber%2)!=(tatgetInverseNumber%2))
{
returnfalse;
}
else
{
returntrue;
}
}
//初始化一个空链表
voidinitLink(PNode&Head)
{
Head=(PNode)malloc(sizeof(NNode));
Head->next=NULL;
}
//判断链表是否为空
boolisEmpty(PNodeHead)
{
if(Head->next==NULL)
{
returntrue;
}
else
{
returnfalse;
}
}
//从链表中拿出一个数据
voidpopNode(PNode&Head,PNode&FNode)
{
if(isEmpty(Head))
{
FNode=NULL;
return;
}
FNode=Head->next;
Head->next=Head->next->next;
FNode->next=NULL;
}
//向节点的最终后继节点链表中添加新的子节点
voidaddSpringNode(PNode&Head,PNodenewData)
{
PSPLinknewNode=(PSPLink)malloc(sizeof(SPLink));
newNode->pointData=newData;
newNode->next=Head->child;
Head->child=newNode;
}
//释放状态图中存放节点后继节点地址的空间
voidfreeSpringLink(PSPLink&Head)
{
PSPLinktmm;
while(Head!=NULL)
{
tmm=Head;
Head=Head->next;
free(tmm);
}
}
//释放open表与closed表中的资源
voidfreeLink(PNode&Head)
{
PNodetmn;
tmn=Head;
Head=Head->next;
free(tmn);
while(Head!=NULL)
{
//首先释放存放节点后继节点地址的空间
freeSpringLink(Head->child);
tmn=Head;
Head=Head->next;
free(tmn);
}
}
//向普通链表中添加一个节点
voidaddNode(PNode&Head,PNode&newNode)
{
newNode->next=Head->next;
Head->next=newNode;
}
//向非递减排列的链表中添加一个节点
voidaddAscNode(PNode&Head,PNode&newNode)
{
PNodeP;
PNodeQ;
P=Head->next;
Q=Head;
while(P!=NULL&&P->f_value<newNode->f_value)
{
Q=P;
P=P->next;
}
//上面判断好位置之后,下面就是简单的插入节点了
newNode->next=Q->next;
Q->next=newNode;
}
//计算节点到目标状态的预计耗散值
intcomputeh_value(PNodetheNode,statustargetStatus)
{
intnum=0;
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
{
if(theNode->data[i][j]!=targetStatus[i][j])
{
num++;
}
}
}
returnnum;
}
//计算节点的f,g,h值
voidcomputeAllValue(PNode&theNode,PNodeparentNode,statustargetStatus)
{
if(parentNode==NULL)
{
theNode->g_value=0;
}
else
{
theNode->g_value=parentNode->g_value+1;
}
theNode->h_value=computeh_value(theNode,targetStatus);
theNode->f_value=theNode->g_value+theNode->h_value;
}
//初始化函数,进行算法初始条件的设置
voidinitial(statusstartStatus,statustargetStatus)
{
//初始化open以及closed表
initLink(open);
initLink(closed);
//初始化起始节点,令初始节点的父节点为空节点
PNodeNULLNode=NULL;
PNodeStartNode=(PNode)malloc(sizeof(NNode));
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
{
StartNode->data[i][j]=startStatus[i][j];
}
}
StartNode->parent=NULL;
StartNode->child=NULL;
StartNode->next=NULL;
computeAllValue(StartNode,NULLNode,targetStatus);
//起始节点进入OPEN表
addAscNode(open,StartNode);
}
//将B节点的状态赋值给A节点
voidstatusAEB(PNode&ANode,PNodeBNode)
{
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
{
ANode->data[i][j]=BNode->data[i][j];
}
}
}
//两个节点是否有相同的状态
boolhasSameStatus(PNodeANode,PNodeBNode)
{
for(inti=0;i<size;i++)
{
for(intj=0;j<size;j++)
{
if(ANode->data[i][j]!=BNode->data[i][j])
returnfalse;
}
}
returntrue;
}
//节点与其祖先节点是否有相同的状态
boolhasAnceSameStatus(PNodeOrigiNode,PNodeAnceNode)
{
while(AnceNode!=NULL)
{
if(hasSameStatus(OrigiNode,AnceNode))
returntrue;
AnceNode=AnceNode->parent;
}
returnfalse;
}
//取得方格中空的格子的位置
voidgetPosition(PNodetheNode,int&row,int&col)
{
for(inti=0;i<size;i++)
{
for(intj=0;j<size;j++)
{
if(theNode->data[i][j]==0)
{
row=i;
col=j;
return;
}
}
}
}
//交换两个数字的值
voidchangeAB(int&a,int&b)
{
intc;
c=b;
b=a;
a=c;
}
//检查相应的状态是否在某一个链表中
boolinLink(PNodespciNode,PNodetheLink,PNode&theNodeLink,PNode&preNode)
{
preNode=theLink;
theLink=theLink->next;
while(theLink!=NULL)
{
if(hasSameStatus(spciNode,theLink))
{
theNodeLink=theLink;
returntrue;
}
preNode=theLink;
theLink=theLink->next;
}
returnfalse;
}
//产生节点的后继节点链表
voidSpringLink(PNodetheNode,PNode&spring,statustargetStatus)
{
introw;
intcol;
getPosition(theNode,row,col);
//空的格子右边的格子向左移动
if(col!=2)
{
PNoderlNewNode=(PNode)malloc(sizeof(NNode));
statusAEB(rlNewNode,theNode);
changeAB(rlNewNode->data[row][col],rlNewNode->data[row][col+1]);
if(hasAnceSameStatus(rlNewNode,theNode->parent))
{
free(rlNewNode);//与父辈相同,丢弃本节点
}
else
{
rlNewNode->parent=theNode;
rlNewNode->child=NULL;
rlNewNode->next=NULL;
computeAllValue(rlNewNode,theNode,targetStatus);
//将本节点加入后继节点链表
addNode(spring,rlNewNode);
}
}
//空的格子左边的格子向右移动
if(col!=0)
{
PNodelrNewNode=(PNode)malloc(sizeof(NNode));
statusAEB(lrNewNode,theNode);
changeAB(lrNewNode->data[row][col],lrNewNode->data[row][col-1]);
if(hasAnceSameStatus(lrNewNode,theNode->parent))
{
free(lrNewNode);//与父辈相同,丢弃本节点
}
else
{
lrNewNode->parent=theNode;
lrNewNode->child=NULL;
lrNewNode->next=NULL;
computeAllValue(lrNewNode,theNode,targetStatus);
//将本节点加入后继节点链表
addNode(spring,lrNewNode);
}
}
//空的格子上边的格子向下移动
if(row!=0)
{
PNodeudNewNode=(PNode)malloc(sizeof(NNode));
statusAEB(udNewNode,theNode);
changeAB(udNewNode->data[row][col],udNewNode->data[row-1][col]);
if(hasAnceSameStatus(udNewNode,theNode->parent))
{
free(udNewNode);//与父辈相同,丢弃本节点
}
else
{
udNewNode->parent=theNode;
udNewNode->child=NULL;
udNewNode->next=NULL;
computeAllValue(udNewNode,theNode,targetStatus);
//将本节点加入后继节点链表
addNode(spring,udNewNode);
}
}
//空的格子下边的格子向上移动
if(row!=2)
{
PNodeduNewNode=(PNode)malloc(sizeof(NNode));
statusAEB(duNewNode,theNode);
changeAB(duNewNode->data[row][col],duNewNode->data[row+1][col]);
if(hasAnceSameStatus(duNewNode,theNode->parent))
{
free(duNewNode);//与父辈相同,丢弃本节点
}
else
{
duNewNode->parent=theNode;
duNewNode->child=NULL;
duNewNode->next=NULL;
computeAllValue(duNewNode,theNode,targetStatus);
//将本节点加入后继节点链表
addNode(spring,duNewNode);
}
}
}
//输出给定节点的状态
voidoutputStatus(PNodestat)
{
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
{
cout<<stat->data[i][j]<<"";
}
cout<<endl;
}
}
//输出最佳的路径
voidoutputBestRoad(PNodegoal)
{
intdeepnum=goal->g_value;
if(goal->parent!=NULL)
{
outputBestRoad(goal->parent);
}
cout<<"第"<<deepnum--<<"步的状态:"<<endl;
outputStatus(goal);
}
voidAStar(statusstartStatus,statustargetStatus)
{
PNodetmpNode;//指向从open表中拿出并放到closed表中的节点的指针
PNodespring;//tmpNode的后继节点链
PNodetmpLNode;//tmpNode的某一个后继节点
PNodetmpChartNode;
PNodethePreNode;//指向将要从closed表中移到open表中的节点的前一个节点的指针
boolgetGoal=false;//标识是否达到目标状态
longnumcount=1;//记录从open表中拿出节点的序号
initial(startStatus,targetStatus);//对函数进行初始化
initLink(spring);//对后继链表的初始化
tmpChartNode=NULL;
cout<<"从OPEN表中拿出的节点的状态及相应的值"<<endl;
while(!isEmpty(open))
{
//从OPEN表中拿出f值最小的元素,并将拿出的元素放入CLOSED表中
popNode(open,tmpNode);
addNode(closed,tmpNode);
cout<<"第"<<numcount++<<"个状态是:"<<endl;
outputStatus(tmpNode);
cout<<"其f值为:"<<tmpNode->f_value<<endl;
cout<<"其g值为:"<<tmpNode->g_value<<endl;
cout<<"其h值为:"<<tmpNode->h_value<<endl;
//如果拿出的元素是目标状态则跳出循环
if(computeh_value(tmpNode,targetStatus)==0)
{
getGoal=true;
break;
}
//产生当前检测节点的后继(与祖先不同)节点列表,产生的后继节点的parent属性指向当前检测的节点
SpringLink(tmpNode,spring,targetStatus);
//遍历检测节点的后继节点链表
while(!isEmpty(spring))
{
popNode(spring,tmpLNode);
//状态在OPEN表中已经存在,thePreNode参数在这里并不起作用
if(inLink(tmpLNode,open,tmpChartNode,thePreNode))
{
addSpringNode(tmpNode,tmpChartNode);
if(tmpLNode->g_value<tmpChartNode->g_value)
{
tmpChartNode->parent=tmpLNode->parent;
tmpChartNode->g_value=tmpLNode->g_value;
tmpChartNode->f_value=tmpLNode->f_value;
}
free(tmpLNode);
}
//状态在CLOSED表中已经存在
elseif(inLink(tmpLNode,closed,tmpChartNode,thePreNode))
{
addSpringNode(tmpNode,tmpChartNode);
if(tmpLNode->g_value<tmpChartNode->g_value)
{
PNodecommu;
tmpChartNode->parent=tmpLNode->parent;
tmpChartNode->g_value=tmpLNode->g_value;
tmpChartNode->f_value=tmpLNode->f_value;
freeSpringLink(tmpChartNode->child);
tmpChartNode->child=NULL;
popNode(thePreNode,commu);
addAscNode(open,commu);
}
free(tmpLNode);
}
//新的状态即此状态既不在OPEN表中也不在CLOSED表中
else
{
addSpringNode(tmpNode,tmpLNode);
addAscNode(open,tmpLNode);
}
}
}
//目标可达的话,输出最佳的路径
if(getGoal)
{
cout<<endl;
cout<<"路径长度为:"<<tmpNode->g_value<<endl;
outputBestRoad(tmpNode);
}
//释放节点所占的内存
freeLink(open);
freeLink(closed);
}
voidmain()
{
//开始状态和目标状态
statusstartStatus={2,8,3,1,6,4,7,0,5};
statustargetStatus={1,2,3,8,0,4,7,6,5};
if(hasSolution(startStatus,targetStatus))
{
AStar(startStatus,targetStatus);
}
else
{
cout<<"从当前输入的初始状态无法经过有限步数变换至您期望的目标状态!"<<endl;
}
system("pause");
}
相关文章推荐
- 源代码:基于A*算法的八数码问题的实现(用OpenGL实现动态演示)
- 源代码:基于A*算法的八数码问题的实现(类的定义与实现)
- A*算法解决八数码(九宫重排)问题
- N数码问题的启发式搜索算法--A*算法python实现
- A*算法解决八数码问题 Java语言实现
- 九宫问题(八数码问题)的解法
- 九宫问题(八数码问题)的解法
- A*算法解决八数码问题 Java语言实现
- A*算法解决八数码问题的C++实现
- A*算法的简单实现(八数码问题)
- A*算法来实现八数码的问题 C++
- A*算法实现八数码问题
- 使用ActionScript3基于Flex实现八数码问题启发式搜索
- 基于python的字符串转整数小程序(手写int()内置函数实现细节)
- Python_猜数字游戏_初次尝试(遗留问题:猜错后程序自动循环执行未实现)---加入循环搞定
- 基于堆实现的优先级队列:PriorityQueue 解决 Top K 问题
- memcpy用指针的实现通过判断地址是否重合解决掉字符串结束符('\0')问题
- VC++编译问题汇总1 单链表的表示和实现,基于c++
- 基于mina实现一个简单数据采集中间件的多客户端在线测试程序
- 用java实现人工智能中的A*算法求8数码问题