[题目描述]
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
[输入]
输入初试状态,一行九个数字,空格用0表示
[输出]
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
[样例输入]
283104765
[样例输出]
4
[参考程序]
program eight;
const
goal='123804765';
vx:array[1..4]of integer=(1,-1,3,-3);
maxn=1200007;
type
rec=record
s:string[9];
step:longint;
end;
re=record
f:boolean;
s:string[9];
end;
var
opt:array[0..maxn] of rec;
flag:array[0..maxn] of re;
i,j,n,m,x,w,k:longint;
v:boolean;
st,y:string[9];
function lhash(s:string):int64; //哈希函数
var
hash:int64;
begin
val(s,hash);
lhash:=hash mod maxn;
end;
procedure judge; //判断是否达到目标
begin
if opt[m].s=goal then
begin
writeln(opt[m].step);
halt;
end;
end;
begin
readln(st); //读入初始值
opt[1].s:=st; //opt[i]表示队列 opt[i].s 是每一步的字符串状态
opt[1].step:=0; //opt[i].step表示步数
x:=lhash(st); //lhash是定义的哈希函数
flag[x].f:=true; //flag[i]是哈希表 flag[i].f是i位置是否有值
flag[x].s:=st; //flag[i].s是i位置的字符
i:=1; j:=1; //i对头 j队尾
while i<=j do //保证队列不空
begin
w:=pos('0',opt[i].s); //找到0的位置来拓展
for k:= 1 to 4 do //有4种移动方法
if ((w mod 3=1)and(k<>2)or((w mod 3=0))and(k<>1))or(w mod 3=2) then //w mod 3=1时0在最左边,不能往左移(k<>2);
//w mod 3=0时0在最右边,不能往右移(k<>1);
if (w+vx[k]>0)and(w+vx[k]<=9) then //保证加上增值后还在范围内 也就是说0要跟范围内的某个数交换
begin //条件满足后开始拓展
y:=opt[i].s; //把要拓展的那串字符给y
y[w]:=y[w+vx[k]];
y[w+vx[k]]:='0'; //这两步是把两个位置的值交换
x:=lhash(y); //x是在哈希表上的位置
v:=false; //v用来判断是否之前已拓展过这个点 即是否与之前重复 v=false即没有重复 v=true即重复了
while (x<maxn)and(flag[x].f)do //flag[x].f=true表明这个位置上已有别的数 要在范围内另找一个位置 x是最终找到的位置
begin
if flag[x].s=y then //如果发现该状态之前已存在,就不入队(v:=true)
begin
v:=true;
break;
end;
inc(x);
end;
if not v then //如果该状态还没有拓展过就入队
begin
inc(j); //队尾+1
m:= j mod maxn; //看起来这步没用 因为不会有如此多部
opt[m].s:=y;
opt[m].step:=opt[i].step+1; //给队尾赋上值
flag[x].f:=true;
flag[x].s:=y; //在x位上修改哈希表
judge; //判断是否到达目标节点
end;
end;
inc(i); //拓展完一个状态 队头+1
end;
end.