问题描述:编号为1~8的8个正方形滑块被摆成3行3列(有一个格子留空),每次可以把与空格相邻的滑块(有公共边才算相邻)移到空格中,而它原来的位置就成为了新的空格。给定初始局面和目标局面(用0表示空格),计算出最少的移动步数。如果无法到达目标局面,则输出-1。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<iostream>
using namespace std;
typedef int State[9]; //定义状态类型
const int MAXSTATE = 1000000;
State st[MAXSTATE], goal; //状态数组。所有状态都保存在这里
int dist[MAXSTATE]; //距离数组
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
//BFS,返回目标状态在st数组下标
//法一:利用编码解码法判重
/*int vis[362880], fact[9];
void init_lookup_table()
{
memset(vis,0,sizeof(vis));
fact[0] = 1;
for(int i = 1; i < 9; i++) fact[i] = fact[i-1] * i;
}
int try_to_insert(int s)
{
int code = 0; //把st[s]映射到整数code
for(int i = 0; i < 9; i++)
{
int cnt = 0;
for(int j = i+1; j < 9; j++) if(st[s][j] < st[s][i]) cnt++;
code += fact[8-i] * cnt;
}
if(vis[code]) return 0;
return vis[code] = 1;
}*/
//法二:利用hash
/*const int MAXHASHSIZE = 1000003;
int head[MAXHASHSIZE], next[MAXSTATE];
void init_lookup_table(){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%MAXHASHSIZE; //确保hash函数值是不超过hash表的大小的非负整数
}
int try_to_insert(int s)
{
int h = hash(st[s]);
int u = head[h];
while(u)
{
if(memcmp(st[u],st[s],sizeof(st[s])) == 0) return 0; //找到了,插入失败
u = next[u]; //顺着链表继续找
}
next[s] = head[h]; //插入到链表中
head[h] = s;
return 1;
}*/
//法三:利用STL中的集合
/*set<int> vis;
void init_lookup_table(){vis.clear();}
int try_to_insert(int s)
{
int v = 0;
for(int i = 0; i < 9; i++) v = v*10+st[s][i];
if(vis.count(v)) return 0;
vis.insert(v);
return 1;
}*/
//声明结构体,重载“括号运算”来比较两个状态。在下面的程序中,整数a和b
//分别是两个状态在状态数组st中的下标,在比较时直接使用memcpy来比较整个内存块。
struct cmp
{
bool operator() (int a,int b) const
{
return memcmp(&st[a],&st[b],sizeof(st[b])) < 0;
}
};
set<int,cmp> vis;
void init_lookup_table() {vis.clear();}
int try_to_insert(int s)
{
if(vis.count(s)) return 0;
vis.insert(s);
return 1;
}
int bfs()
{
init_lookup_table();
int front = 1, rear = 2;
while(front < rear)
{
State& s = st[front];
if(memcmp(goal,s,sizeof(s)) == 0) return front; //找到目标状态,成功返回
int z;
for(z = 0; z < 9; z++) if(!s[z]) break; //找到“0”所在的位置
int x = z/3, y = z%3;
for(int d = 0; d < 4; d++)
{
int newx = x + dx[d];
int newy = y + dy[d];
int newz = newx * 3 + newy;
if(newx >= 0 && newy < 3 && newy >= 0 && newy < 3) //若移动合法
{
State& t = st[rear];
memcpy(&t, &s, sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
dist[rear] = dist[front] + 1;
if(try_to_insert(rear)) rear++;
}
}
front++;
}
return 0;
}
int main()
{
freopen("D:\\1.txt","r",stdin);
for(int i = 0; i < 9; i++) scanf("%d",&st[1][i]);
for(int i = 0; i < 9; i++) scanf("%d",&goal[i]);
dist[1] = 0;
int ans = bfs();
if(ans>0) printf("%d\n",dist[ans]);
else printf("-1\n");
return 0;
}