编号为1-8的8个正方形各自被摆成3*3的样子,有一个空格,可以像华容道一样将旁边的正方形滑入空格处,现给出这个格子初始的状态和结束时的状态,最少经过多少次变换才能变换成为最终状态。
与其说这是一种算法,不如说这是一种认知角度或者说思维,将每种八数码当前的形式看做一个状态,这个状态可以由线性的序列确定,然后从开始的状态进行搜索,不断通过可行的变换生成新的状态,只需找到到达最终状态的最短路径即可,那么我们当然是用bfs来解决,之所以叫隐式图搜索是因为和之前直接给出的图不同,隐式图的每个节点其实是我们将当前的状态认为作节点,生成的图。
其实总体上来说就是一个很普通的bfs,但是由于每个节点和普通的bfs节点不同,八数码每个节点就是一张图,这就涉及到储存和查重的问题了,储存的话我们可以用编号法用一维的数组储存二位的八数码状态当做一个节点,查重是本题的重点也是难点,我们的解决思路是将状态编码成一个十进制的数,然而如果用一个9位的数组来保存每种状态是否出现过的话,那么大量的位置将会被浪费掉,所以这里我们使用hash(除法)的方法,来保存判断这个状态是否出现过。
数据:
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
#include <iostream> #include <cstring> #include <cstdio> using namespace std; int dx[]={-1,1,0,0}; int dy[]={0,0,-1,1}; int const maxstates=1000000; typedef int states[9]; states st[maxstates],goal; int dist[maxstates]; const int hashsize=1000010; int book[hashsize],next[hashsize]; void init() { memset(book,0,sizeof(book)); memset(next,0,sizeof(next)); } int hash(states &s) { int v=0; for(int i=0;i<9;i++) v=v*10+s[i]; return v%hashsize; } int try_to_insert(int s) { int h=hash(st[s]); int u=book[h]; while(u) { if(memcmp(st[s],st[u],sizeof(st[s]))==0) return 0; u=next[u]; } next[u]=book[h]; book[h]=s; } int BFS() { init(); int head,tail; head=tail=1; tail++; while(head<tail) { states &s=st[head]; if(memcmp(goal,s,sizeof(s))==0) { return head; } int z,x,y; for(int i=0;i<9;i++) if(s[i]==0) {z=i;break;} x=z/3,y=z%3; for(int i=0;i<4;i++) { int tempx=x+dx[i]; int tempy=y+dy[i]; if(tempx>=0&&tempx<3&&tempy>=0&&tempy<3) { int tempz=tempx*3+tempy; states &t=st[tail]; memcpy(&t,&s,sizeof(s)); t[z]=t[tempz]; t[tempz]=0; dist[tail]=dist[head]+1; if(try_to_insert(tail)) tail++; } } head++; } return -1; } int main() { //freopen("input.txt","r",stdin); for(int i=0;i<9;i++) cin>>st[1][i]; for(int i=0;i<9;i++) cin>>goal[i]; memset(dist,0,sizeof(dist)); int ans=BFS(); if(ans>0) cout<<dist[ans]<<endl; else cout<<-1<<endl; return 0; }