八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
人工智能课的作业。感觉一点都不智能。。
算法核心是bfs hash用的是康拓展开
输入是第一行是初始状态,第二行是终止状态,空格用0表示。
012
345 ———> 012345678
678
一些测试数据:
012345678
102345678
103425678
123405678
123045678
123405678
123475608
123405678
123450678
123405678
234150768
123456780
结果:
最少步数为:1
路径为:r
最少步数为:1
路径为:d
最少步数为:1
路径为:r
最少步数为:1
路径为:u
最少步数为:1
路径为:l
最少步数为:19
路径为:ullddrurdllurdruldr
下面贴两个版本的代码
裸BFS版
参考kuangbin的HDU1043的代码写的,因为自己原先写了一个觉得很丑,还是大佬的姿势比较优美。
#include "cstdio"
#include "queue"
#include "cstring"
#include "string.h"
#include "iostream"
#include "algorithm"
#include "cmath"
#include "vector"
using namespace std;
const int MAXN=1000000;
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//康拖展开判重
// 0!1!2!3! 4! 5! 6! 7! 8! 9!
bool vis[MAXN];//标记
string path;
int aim;//123456780对应的康拖展开的hash值
int aimmap[3][3];
int mymove[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r
char indexs[5]="udlr";//正向搜索
int cantor(int s[])//康拖展开求该序列的hash值
{
int sum=0;
for(int i=0;i<9;i++)
{
int num=0;
for(int j=i+1;j<9;j++)
if(s[j]9-i-1]);
}
return sum+1;
}
struct Node
{
int s[9];
int loc;//“0”的位置
int status;//康拖展开的hash值
int step;//统计步数 也用作gx
int hx;
string path;//路径
void gethx()
{
int temp=0;
for(int i=0;i<9;i++)
{
int cur_x=i/3;
int cur_y=i%3;
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
{
if(aimmap[j][k]==s[i])
{
temp+=abs(j-cur_x)+abs(k-cur_y);
}
}
}
}
hx=temp;
}
};
struct cmp //优先队列需要的排序函数,升序
{
bool operator()(Node &a,Node &b)
{
int aa=a.hx+a.step;
int bb=b.hx+b.step;
return aa>bb;
}
};
Node ncur;
bool bfs()
{
memset(vis,false,sizeof(vis));
Node cur,next;
priority_queuevector,cmp>q;
ncur.gethx();
q.push(ncur);
while(!q.empty())
{
cur=q.top();
q.pop();
if(cur.status==aim)
{
path=cur.path;
return true;
}
int x=cur.loc/3;
int y=cur.loc%3;
for(int i=0;i<4;i++)
{
int tx=x+mymove[i][0];
int ty=y+mymove[i][1];
if(tx<0||tx>2||ty<0||ty>2)continue;
next=cur;
next.loc=tx*3+ty;
next.s[cur.loc]=next.s[next.loc];
next.s[next.loc]=0;
next.status=cantor(next.s);
if(!vis[next.status])
{
next.step++;
ncur.gethx();
vis[next.status]=true;
next.path=next.path+indexs[i];
if(next.status==aim)
{
cout<<"最少步数为:"<return true;
}
q.push(next);
next.step--;
}
}
}
return false;
}
int main()
{
char start;
int myaim[9];
while(1)
{
ncur.step=0;
for(int i=0;i<9;i++)
{
cin>>start;
if(start=='0')
ncur.loc=i;
ncur.s[i]=start-'0';
}
ncur.status=cantor(ncur.s);
for(int i=0;i<9;i++)
{
char to;
cin>>to;
myaim[i]=to-'0';
int cur_x=i/3;
int cur_y=i%3;
aimmap[cur_x][cur_y]=to-'0';
}
aim=cantor(myaim);
ncur.status=cantor(ncur.s);
if(bfs())
{
cout<<"路径为:"<else cout<<"无解"<cout<return 0;
}
A*版
h(x)用的是到当前状态的步数
g(x)是用的当前每个格子中的数到最终位置的曼哈顿距离(街区距离)的和,然后优先队列维护一下就好了。
#include "cstdio"
#include "queue"
#include "cstring"
#include "string.h"
#include "iostream"
#include "algorithm"
#include "cmath"
#include "vector"
using namespace std;
const int MAXN=1000000;
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//康拖展开判重
// 0!1!2!3! 4! 5! 6! 7! 8! 9!
bool vis[MAXN];//标记
string path;
int aim;//123456780对应的康拖展开的hash值
int aimmap[3][3];
int mymove[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r
char indexs[5]="udlr";//正向搜索
int cantor(int s[])//康拖展开求该序列的hash值
{
int sum=0;
for(int i=0;i<9;i++)
{
int num=0;
for(int j=i+1;j<9;j++)
if(s[j]9-i-1]);
}
return sum+1;
}
struct Node
{
int s[9];
int loc;//“0”的位置
int status;//康拖展开的hash值
int step;//统计步数 也用作gx
int hx;
string path;//路径
void gethx()
{
int temp=0;
for(int i=0;i<9;i++)
{
int cur_x=i/3;
int cur_y=i%3;
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
{
if(aimmap[j][k]==s[i])
{
temp+=abs(j-cur_x)+abs(k-cur_y);
}
}
}
}
hx=temp;
}
};
struct cmp //优先队列需要的排序函数,升序
{
bool operator()(Node &a,Node &b)
{
int aa=a.hx+a.step;
int bb=b.hx+b.step;
return aa>bb;
}
};
Node ncur;
bool bfs()
{
memset(vis,false,sizeof(vis));
Node cur,next;
priority_queuevector,cmp>q;
ncur.gethx();
q.push(ncur);
while(!q.empty())
{
cur=q.top();
q.pop();
if(cur.status==aim)
{
path=cur.path;
return true;
}
int x=cur.loc/3;
int y=cur.loc%3;
for(int i=0;i<4;i++)
{
int tx=x+mymove[i][0];
int ty=y+mymove[i][1];
if(tx<0||tx>2||ty<0||ty>2)continue;
next=cur;
next.loc=tx*3+ty;
next.s[cur.loc]=next.s[next.loc];
next.s[next.loc]=0;
next.status=cantor(next.s);
if(!vis[next.status])
{
next.step++;
ncur.gethx();
vis[next.status]=true;
next.path=next.path+indexs[i];
if(next.status==aim)
{
cout<<"最少步数为:"<return true;
}
q.push(next);
next.step--;
}
}
}
return false;
}
int main()
{
char start;
int myaim[9];
while(1)
{
ncur.step=0;
for(int i=0;i<9;i++)
{
cin>>start;
if(start=='0')
ncur.loc=i;
ncur.s[i]=start-'0';
}
ncur.status=cantor(ncur.s);
for(int i=0;i<9;i++)
{
char to;
cin>>to;
myaim[i]=to-'0';
int cur_x=i/3;
int cur_y=i%3;
aimmap[cur_x][cur_y]=to-'0';
}
aim=cantor(myaim);
ncur.status=cantor(ncur.s);
if(bfs())
{
cout<<"路径为:"<else cout<<"无解"<cout<return 0;
}