解八数码问题。
/*编号为1-8的8个正方形滑块被摆成3行3列(空一格),
每次可以把与空格相邻的滑块移到空格中!
而它原来的位置就变成了空格!
任务就是计算出最少的移动步数,如果无法移动到目标局面,就输出-1!
*/
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1001314;
int dx[4]= {1,0,-1,0};
int dy[4]= {0,1,0,-1};
int head[maxn],next[maxn],dis[maxn];
typedef int A[9];
A aa[maxn],goal;
void init()
{
memset(head,0,sizeof(head));
}
int hash(A& s)
{
int sum=0;
for(int i=0; i<9; i++)
{
sum=sum*10+s[i];
}//把九个数组合成9位数
return sum%maxn;//确保hash函数值是不超过hash表的大小的非负整数
}
bool insert(int s)//使用哈希表、
{
int h=hash(aa[s]);//从表头开始查找链表
int u=head[h];
while(u)
{
if(memcmp(aa[u],aa[s],sizeof(aa[s]))==0)
return false;//找到了,插入失败
u=next[u];//顺着链表继续找
}
next[s]=head[h];//插入到链表中
head[h]=s;
return true;
}
int bfs()
{
init();//初始化
int begin=1,end=2;
while(begin<end)
{
A& now=aa[begin];
if(memcmp(now,goal,sizeof(now))==0)//如果找到最终状态,则返回最终状态的下标。
return begin;
int point;
for(point=0; point<9; point++)
if(now[point]==0)//找到0所在的编号
break;
for(int i=0; i<4; i++)
{
int x=point/3,y=point%3;
int new_x=x+dx[i];
int new_y=y+dy[i];
int new_point=new_x*3+new_y;//新滑块的编号
if(new_x>=0&&new_y>=0&&new_x<3&&new_y<3)
{//如果没有越出边界
A& te=aa[end];
memcpy(&te,&now,sizeof(now));
te[point]=now[new_point];
te[new_point]=now[point];
//交换新滑块和空格的位置,即编号
dis[end]=dis[begin]+1;//更新步数。
if(insert(end))
end++;
}
}
begin++;
}
return 0;//没有办法变成最终状态
}
int main()
{
for(int i=0; i<9; i++)
cin>>aa[1][i];//起始状态
for(int i=0; i<9; i++)
cin>>goal[i];//最终状态
int ans=bfs();//返回最终状态的下标
if(ans>0)
cout<<dis[ans]<<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
*/