八数码难题
3×3九宫棋盘,放置数码为1 - 8的8个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布局。
求解的问题——给定初始布局(即初始状态)和目标布局(即目标状态),如何移动棋牌才能从初始布局到达目标布局
解答路径——就是一个合法的走步序列
用宽度优先搜索方法解决该问题:
为问题状态的表示建立数据结构:3×3的一个矩阵,
* 矩阵元素S ij∈{0,1,…,8};其中1≤i,j≤3,
* 数字0指示空格,
* 数字1 - 8指示相应棋牌。
初始状态S0: 2 3 目标状态Sg: 1 2 3
1 8 4 8 0 4
7 6 5 7 6 5
制定操作算子集:
* 直观方法——为每个棋牌制定一套可能的走步:左、上、右、下四种移动。这样就需32个操作算子。
*简易方法——仅为空格制定这4种走步,因为只有紧靠空格的棋牌才能移动。
* 空格移动的唯一约束是不能移出棋盘。
**************************************** A*算法应用 *****************************************************
估价函数:f(n)=d(n)+w(n)
其中:d(n)为n的深度 w(n)为不在位的棋子数
根据f(n)的大小全局择优搜索
取h(n)=w(n),则有w(n)≤h*(n),h(n)满足A*算法的限制条件
在编程中注意:
1:深度和前驱节点的记录。(队列队首元素和队尾元素的下标的作用。)
2:采用宽度优先搜索,再根据A*算法,从open表中选出相对函数值最小的节点进行拓展。
注意深度优先搜索不适合,可能选择的一个分支到最后无解。
3:递归算法应用
*/
以下是代码:
#include
#include
#define N 1000
using namespace std;
int sum=0;
int head=0,step=0;
int m=0,ta=0;
int zuixiao[N];
char cdir[4]={'u','r','d','l'};
char opcdir[4]={'d','l','u','r'};
const int aim_state[9]={1,2,3,8,0,4,7,6,5};
const int aim_loc[9]={4,0,1,2,5,8,7,6,3};
struct bashuma
{
int ccstate[9];
int a;
char fx;
int flag;
int pre;
int cengshu;
};
inline int Abs(int a)
{
return a>0?a:-a;
}
inline int Manh(int loc1,int loc2)
{
return Abs(loc1/3-loc2/3)+Abs(loc1%3-loc2%3);
}
inline int h(int state[9])
{
int ans=0;
for(int i=0;i<9;i++)
if(state[i])
ans+=Manh(i,aim_loc[state[i]]);
return ans;
}
inline int check(int state[9])
{
for(int i=0;i<9;i++)
if(state[i]!=aim_state[i])
return 0;
return 1;
}
inline bool canMove(int loc,int dir,int&nxt)
{
switch(dir)
{
case 0:
nxt=loc-3;
return loc>2;
case 1:
nxt=loc+1;
return loc%3!=2;
case 2:
nxt=loc+3;
return loc<6;
case 3:
nxt=loc-1;
return loc%3;
}
}
bashuma chushihua(bashuma bsm[])
{
int k;
int bsmchushihua[9]={3,1,8,7,6,4,0,2,5};
for(k=0;k<9;k++)
{
bsm[0].ccstate[k]=bsmchushihua[k];
}
bsm[0].a=h(bsm[0].ccstate);
bsm[0].fx=0;
bsm[0].flag=1;
bsm[0].pre=-1;
bsm[0].cengshu=0;
return bsm[0];
}
void rudui1(bashuma bsm[],bashuma bsm1[],char c)
{ int k;
ta++;
for(k=0;k<9;k++)
{
bsm1[ta].ccstate[k]=bsm[head].ccstate[k];
}
bsm1[ta].fx=c;
bsm1[ta].flag=0;
if(m==1){zuixiao[m-1]==-1;bsm1[ta].pre=zuixiao[m-1];}
else
{
bsm1[ta].pre=zuixiao[m-1];}
bsm1[ta].a=h(bsm[head].ccstate)+bsm1[bsm1[ta].pre].cengshu;
bsm1[ta].cengshu=bsm1[bsm1[ta].pre].cengshu+1;
}
void rudui(bashuma bsm[],bashuma bsm1[],int i)
{
head++;
int k;
for(k=0;k<9;k++)
{
bsm[head].ccstate[k]=bsm1[i].ccstate[k];
}
bsm[head].a=bsm1[i].a;
bsm[head].fx=bsm1[i].fx;
bsm[head].pre=i;
}
int min(bashuma bsm[])
{
int smallest;
int i;
smallest = 100000;
for( i =1; i <=ta; i++)
{ if(bsm[i].flag==1)continue;
if(smallest > bsm[i].a)
smallest = bsm[i].a;
}
for ( i = 1; i <=ta; i++)
{ if(bsm[i].flag==1)continue;
if(smallest ==bsm[i].a)
return i;
}
}
int locate(bashuma bsm[])
{ int k;
for(k=0;k<9;k++)
{
if(bsm[head].ccstate[k]==0)
return k;
}
}
int bianli1(bashuma bsm[],bashuma bsm1[])
{
int k,j,l;
for(k=0;k<=ta;k++)
{
l=0;
for(j=0;j<9;j++)
{
if(bsm1[k].ccstate[j]==bsm[head].ccstate[j])
l++;
}
if(l==9)
return 0;
else return 1;
}
}
int pp(bashuma bsm[],bashuma bsm1[])
{ int c,k,j,flag,n,a;
int nxt,now;
while(!check(bsm[head].ccstate))
{
now=locate(bsm);
for(int dir=0;dir<4;dir++)
{
if(head==0)
if(canMove(now,dir,nxt))
{
bsm[head].ccstate[now]=bsm[head].ccstate[nxt];
bsm[head].ccstate[nxt]=0;
rudui1(bsm,bsm1,cdir[dir]);
bsm[head].ccstate[nxt]=bsm[head].ccstate[now];
bsm[head].ccstate[now]=0;
}
if((head!=0)&&(bsm[head].fx!=opcdir[dir])&&canMove(now,dir,nxt))
{
bsm[head].ccstate[now]=bsm[head].ccstate[nxt];
bsm[head].ccstate[nxt]=0;
if(bianli1(bsm,bsm1)){rudui1(bsm,bsm1,cdir[dir]);}
bsm[head].ccstate[nxt]=bsm[head].ccstate[now];
bsm[head].ccstate[now]=0;
}
}
zuixiao[m]=min(bsm1);
a=zuixiao[m];
bsm1[a].flag=1;
m++;
rudui(bsm,bsm1,a);
}
if(check(bsm[head].ccstate))
return 1;
else return 0;
}
void shuchu(bashuma bsm[],int i)
{ int k;
if(bsm[i].pre==-1)
return;
if(bsm[i].pre!=-1)
shuchu(bsm,bsm[i].pre);
printf("\n");
for(k=0;k<9;k++)
{
if(k%3==0)printf("\n");
printf("%d ",bsm[i].ccstate[k]);
}
step++;
printf("\n");
}
int main()
{
int i,k,s;
bashuma bsm[N],bsm1[N];
bsm[0]=chushihua(bsm);
for(k=0;k<9;k++)
{
if(k%3==0)printf("\n");
printf("%d ",bsm[0].ccstate[k]);
}
bsm1[0]=chushihua(bsm1);
i=pp(bsm,bsm1);
if(i)
{
s=bsm[head].pre;
shuchu(bsm1,s);
printf("一共需要走%d步到达目标状态!\n",step+1);
}
else
printf("在深度限制之下没有找到解!\n");
return 0;
}
八数码中同时使用了状态空间法,很欣赏的一个亮点便是操作集是空格移动的位置。分为上下左右四步,化繁为简。
同时采用了A*算法,根据不在位的棋子数确定扩展节点。
贴上初始状态为此:
![这里写图片描述](http:
![这里写图片描述](http:
运行结果如下:![这里写图片描述](http: