题目大意:
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
思路:
BFS+HASH
用bfs求出正确答案,hash判重。
真不知道为什么洛谷评为 提高+/省选- 难度
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int dx[5]={0,0,0,-1,1};
const int dy[5]={0,1,-1,0,0};
const int k=362881;
int x,y,ok,sum,head,tail,q;
int state[k][4][4],father[k],a[4][4],ha[k];
int locate(int x) //哈希判重
{
int y=0;
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
y=y*10+state[x][i][j];
int t=y%k;
int i=0;
while (i<k&&ha[(t+i)%k]!=0&&ha[(t+i)%k]!=y) i++;
if (ha[(t+i)%k]==0)
{
ha[(t+i)%k]=y;
return 0;
}
else return 1;
}
void print(int x) //输出
{
if (father[x]==0) return;
print(father[x]);
sum++;
return;
}
void bfs() //广搜
{
father[1]=0;
head=0;
tail=1;
do
{
head++;
for (int i=1;i<=4;i++)
{
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++)
{
if (state[head][j][k]==0)
{
x=j;
y=k;
break;
}
}
if (x+dx[i]<1||x+dx[i]>3||y+dy[i]<1||y+dy[i]>3)
continue; //出界
tail++;
father[tail]=head;
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++)
state[tail][j][k]=state[head][j][k];
state[tail][x][y]=state[tail][x+dx[i]][y+dy[i]];
state[tail][x+dx[i]][y+dy[i]]=0; //交换(移动)
if (locate(tail)==1) tail--; //判重
ok=1;
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++)
if (state[tail][j][k]!=a[j][k])
{
ok=0;
break;
}
if (ok==1) //达到目标
{
print(tail); //输出
tail=-1;
return;
}
}
}
while(head<tail);
}
int main()
{
a[1][1]=1;a[1][2]=2;a[1][3]=3;
a[2][1]=8;a[2][2]=0;a[2][3]=4;
a[3][1]=7;a[3][2]=6;a[3][3]=5; //初始化
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
scanf("%1d",&state[1][i][j]);
for (int i=1;i<=3;i++)
for (int j=1;j<=3;j++)
{
q++;
if (q==9) q=0;
if (state[1][i][j]!=q) break;
if (q==0) //特判
{
puts("0");
return 0;
}
}
locate(1); //保存初始情况
bfs();
printf("%d\n",sum);
return 0;
}