8数码问题
题意:在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
基本的框架不难确定,这道题目就是一个状态空间搜索的题目。运用基本的BFS就可以搞定。
把当前已经造成的局面看成一个节点,然后往下扩展就行了。
但是这道问题的关键在于如何去判重。
除了可以用集合外,我们可以把每一个状态进行编码方便我们去管理。
那问题就在于如何去进行编码。
那么就用运用散列表hash来保存 具体的哈希函数怎么去设计呢?对于这道题目就直接可以采用把他们编码为一个不超过b的一个整数
这样就可以做了。
大体框架就是这样,下面来讨论具体细节的实现。
首先是节点的扩展。
这就非常的nice了,八数码中我们可以移动的就是0的位置,所以我们应该如何找到z的坐标呢?那么根据数学规律,即可得到x=z/3;y=z%3;这是以0为下标的数组。
好的具体就可以实现了。
最有意思的是typedef的妙用以及整个数组的比较函数和赋值函数。
#include<bits/stdc++.h>
#define maxn 1000000
using namespace std;
typedef int State[9];
State st[maxn],goul={1,2,3,8,0,4,7,6,5};
int head[maxn],next[maxn];
void initLookupTable(){memset(head,0,sizeof(head));}
int hash(State& s){
int v=0;
for(int i=0;i<9;i++)v=v*10+s[i];
return v%1000003;
}
bool tryToInsert(int s){
int h=hash(st[s]);
int u=head[h];
while(u){
if(memcmp(st[u],st[s],sizeof(st[s]))==0)return false;
u=next[u];
}
next[s]=head[h];
head[h]=s;
}
int dist[maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int bfs(){
initLookupTable();
int front=1,rear=2;
while(front<rear){
State &s=st[front];
if(memcmp(goul,s,sizeof(s))==0)return front;
int z;
for(z=0;z<9;z++)if(!s[z])break;
int x=z/3;int y=z%3;
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
int nz=nx*3+ny;
if(nx>=0&&nx<3&&ny>=0&&ny<3){
State& t=st[rear];
memcpy(&t,&s,sizeof(s));
t[nz]=s[z];
t[z]=s[nz];
dist[rear]=dist[front]+1;
if(tryToInsert(rear))rear++;
}
}
front++;
}
return 0;
}
int main(){
char sts[9];cin>>sts;
for(int i=0;i<9;i++)st[1][i]=sts[i]-'0';
int ans=bfs();
printf("%d",dist[ans]);
return 0;
}