问题描述:
在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。这些数码可以在棋盘上移动,其移动规则是:与空格相邻的数码方格可以移入空格。现在的问题是:对于指定的初始棋局和目标棋局,给出数码的移动序列。该问题称八数码难题或者重排九宫问题。
问题思路:
这可以看做是一个隐式图搜索问题,把每一次移动一格子的分布认为是一个状态图,空格记为0,并认为移动格子的时候,移动的是0这一个格子和另外的格子交换。我在这里使用典型的BFS算法来判断是否能到达目标状态,并且给出多少步。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef int state[9];
const int maxn=1000000;
state st[maxn],goal;
int dist[maxn];
int front,rear;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
set<int> vis;
int try_to_insert(int pos)//判断重复
{
int tmp=0;
for(int i=0;i<9;i++) tmp=tmp*10+st[pos][i];//把状态图计算为一个9位的独一无二的数字存在set中
if(vis.count(tmp)) return 0;
vis.insert(tmp);
return 1;
}
int bfs()
{
vis.clear();
front=1,rear=2;
while(front<rear)
{
state &s=st[front];
if(memcmp(goal,s,sizeof(s))==0) return front;
int i,j,x,y;
for(i=0;i<9;i++) if(s[i]==0) break;//找到0这一格所在位置,把状态的变化看作是0这一格的移动
x=i/3,y=i%3;
for(j=0;j<4;j++)
{
int newx=x+dx[j];
int newy=y+dy[j];
if(newx>=0&&newx<3&&newy>=0&&newy<3)
{
state &v=st[rear];
memcpy(&v,&s,sizeof(s));
v[x*3+y]=v[newx*3+newy];
v[newx*3+newy]=0;
dist[rear]=dist[front]+1;
if(try_to_insert(rear)) rear++;//判断是否出现重复状态
}
}
front++;
}
}
int main()
{
for(int i=0;i<9;i++) cin>>st[1][i];
for(int i=0;i<9;i++) cin>>goal[i];
int d=bfs();
if(d>0) cout<<dist[d]<<endl;
else cout<<"-1"<<endl;
return 0;
}
输入:
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
输出:
31