八数码问题的两种解法
这个题可以用A*算法,这里不深究。
主要讲解一下普通BFS+hash判重、双向BFS。
1)普通BFS+hash判重
普通BFS必须hash判重。如果不判重会出现TLE的杯具(亲历……)
具体是这么实现的:每次枚举可以到达的状态,如果不是目标状态,并且没有入队,那么就加入队列,直到找到目标状态为止。在这里用的Cantor展开判重。
[pascal 代码]
Const
aim='123804765';
VAR
q:array[1..100000]of string;
step:array[1..100000]of longint;
facs:array[1..100]of longint;
vis:array[0..362881]of boolean;
st:string;
head,tail:longint;
Function fac(p:longint):longint;
var
i:longint;
begin
if facs[p]>0 then exit(facs[p]);
fac:=1;
for i:=p downto 2 do fac:=fac*i;
facs[p]:=fac;
end;
Function cantor(s:string):longint;
var
i,j,tmp,num,n:longint;
begin
n:=9;
num:=0;
for i:=1 to n-1 do
begin
tmp:=0;
for j:=i+1 to n do if s[j]<s[i] then inc(tmp);
num:=num+fac(n-i)*tmp;
end;
cantor:=num+1;
end;
Function change(st:string;x,y:longint):string;
var
tmp:char;
s:string;
begin
s:=st;
tmp:=s[x];
insert(s[y],s,x);
delete(s,x+1,1);
insert(tmp,s,y+1);
delete(s,y,1);
exit(s);
end;
Function moveleft(st:string):string;
var
i,position:longint;
s:string;
begin
position:=0;
for i:=1 to 9 do if st[i]='0' then
begin
position:=i;
break;
end;
s:=change(st,position,position-1);
exit(s);
end;
Function moveright(st:string):string;
var
i,position:longint;
s:string;
begin
position:=0;
for i:=1 to 9 do if st[i]='0' then
begin
position:=i;
break;
end;
s:=change(st,position,position+1);
exit(s);
end;
Function moveup(st:string):string;
var
i,position:longint;
s:string;
begin
position:=0;
for i:=1 to 9 do if st[i]='0' then
begin
position:=i;
break;
end;
s:=change(st,position,position-3);
exit(s);
end;
Function movedown(st:string):string;
var
i,position:longint;
s:string;
begin
position:=0;
for i:=1 to 9 do if st[i]='0' then
begin
position:=i;
break;
end;
s:=change(st,position,position+3);
exit(s);
end;
Function canmoveleft(s:string):boolean;
var
i,position:longint;
begin
position:=0;
for i:=1 to 9 do if s[i]='0' then
begin
position:=i;
break;
end;
canmoveleft:=true;
if position=1 then exit(false);
if position=4 then exit(false);
if position=7 then exit(false);
end;
Function canmoveright(s:string):boolean;
var
i,position:longint;
begin
position:=0;
for i:=1 to 9 do if s[i]='0' then
begin
position:=i;
break;
end;
canmoveright:=true;
if position=3 then exit(false);
if position=6 then exit(false);
if position=9 then exit(false);
end;
Function canmoveup(s:string):boolean;
var
i,position:longint;
begin
position:=0;
for i:=1 to 9 do if s[i]='0' then
begin
position:=i;
break;
end;
canmoveup:=true;
if position=1 then exit(false);
if position=2 then exit(false);
if position=3 then exit(false);
end;
Function canmovedown(s:string):boolean;
var
i,position:longint;
begin
position:=0;
for i:=1 to 9 do if s[i]='0' then
begin
position:=i;
break;
end;
canmovedown:=true;
if position=7 then exit(false);
if position=8 then exit(false);
if position=9 then exit(false);
end;
Procedure enque(s:string);
var
k:longint;
begin
k:=cantor(s);
if vis[k] then exit;
vis[k]:=true;
inc(tail);
if tail>100000 then tail:=1;
q[tail]:=s;
step[tail]:=step[head]+1;
end;
Procedure print;
begin
writeln(step[head]+1);
halt;
end;
Function outque:string;
begin
inc(head);
if head>100000 then head:=1;
exit(q[head]);
end;
Procedure bfs;
var
pre,tmp:string;
begin
head:=0;tail:=1;q[1]:=st;step[1]:=0;
while head<tail do
begin
pre:=outque;
if canmoveleft(pre) then
begin
tmp:=moveleft(pre);
if tmp<>aim then enque(tmp) else print;
end;
if canmoveright(pre) then
begin
tmp:=moveright(pre);
if tmp<>aim then enque(tmp) else print;
end;
if canmoveup(pre) then
begin
tmp:=moveup(pre);
if tmp<>aim then enque(tmp) else print;
end;
if canmovedown(pre) then
begin
tmp:=movedown(pre);
if tmp<>aim then enque(tmp) else print;
end;
end;
end;
BEGIN
fillchar(facs,sizeof(facs),0);
fillchar(vis,sizeof(vis),false);
readln(st);
bfs;
END.
2)双向BFS+暴力判重(速度极快),如果用hash会更快,不过在这里我感觉不必了……
具体实现方法:
用了双向广搜的思想,从初始状态和目标状态同时搜索,直到出现同样的状态(即相遇),那么就输出结果。每次搜索时队尾小的队列先扩展。
[pascal 代码]
Const
aim='123804765';
maxq=100000;
Var
head,tail:array[0..1]of longint;
q:array[0..1,1..maxq]of string;
step:array[0..1,1..maxq*2]of longint;
st:string;
Procedure init;
begin
readln(st);
end;
Function canmoveleft(k:longint):boolean;
begin
if k=1 then exit(false);
if k=4 then exit(false);
if k=7 then exit(false);
exit(true);
end;
Function canmoveright(k:longint):boolean;
begin
if k=3 then exit(false);
if k=6 then exit(false);
if k=9 then exit(false);
exit(true);
end;
Function canmoveup(k:longint):boolean;
begin
if k=1 then exit(false);
if k=2 then exit(false);
if k=3 then exit(false);
exit(true);
end;
Function canmovedown(k:longint):boolean;
begin
if k=7 then exit(false);
if k=8 then exit(false);
if k=9 then exit(false);
exit(true);
end;
Function moveleft(s:string;k:longint):string;
var
tmp:string;
i:longint;
begin
tmp:='';
for i:=1 to k-2 do tmp:=tmp+s[i];
tmp:=tmp+s[k];
tmp:=tmp+s[k-1];
for i:=k+1 to 9 do tmp:=tmp+s[i];
exit(tmp);
end;
Function moveright(s:string;k:longint):string;
var
tmp:string;
i:longint;
begin
tmp:='';
for i:=1 to k-1 do tmp:=tmp+s[i];
tmp:=tmp+s[k+1];
tmp:=tmp+s[k];
for i:=k+2 to 9 do tmp:=tmp+s[i];
exit(tmp);
end;
Function moveup(s:string;k:longint):string;
var
tmp:string;
i:longint;
begin
tmp:='';
for i:=1 to k-4 do tmp:=tmp+s[i];
tmp:=tmp+s[k];
for i:=k-2 to k-1 do tmp:=tmp+s[i];
tmp:=tmp+s[k-3];
for i:=k+1 to 9 do tmp:=tmp+s[i];
exit(tmp);
end;
Function movedown(s:string;k:longint):string;
var
tmp:string;
i:longint;
begin
tmp:='';
for i:=1 to k-1 do tmp:=tmp+s[i];
tmp:=tmp+s[k+3];
for i:=k+1 to k+2 do tmp:=tmp+s[i];
tmp:=tmp+s[k];
for i:=k+4 to 9 do tmp:=tmp+s[i];
exit(tmp);
end;
Function vis(s:string;t:byte):boolean;
var
i:longint;
begin
for i:=1 to tail[t] do if q[t,i]=s then exit(true);
exit(false);
end;
Procedure print(k:longint);
begin
writeln(k);
halt;
end;
Procedure check(t:byte);
var
i:longint;
begin
for i:=1 to tail[1-t] do if q[1-t,i]=q[t,tail[t]] then print(step[1-t,i]+step[t,tail[t]]);
end;
Procedure expand(t:byte);
var
i,j,pre:longint;
pres,tmp:string;
begin
inc(head[t]);pres:=q[t,head[t]];
pre:=pos('0',pres);
if canmoveleft(pre) then
begin
tmp:=moveleft(pres,pre);
if not vis(tmp,t) then
begin
inc(tail[t]);
q[t,tail[t]]:=tmp;
step[t,tail[t]]:=step[t,head[t]]+1;
end;
check(t);
end;
if canmoveright(pre) then
begin
tmp:=moveright(pres,pre);
if not vis(tmp,t) then
begin
inc(tail[t]);
q[t,tail[t]]:=tmp;
step[t,tail[t]]:=step[t,head[t]]+1;
end;
check(t);
end;
if canmoveup(pre) then
begin
tmp:=moveup(pres,pre);
if not vis(tmp,t) then
begin
inc(tail[t]);
q[t,tail[t]]:=tmp;
step[t,tail[t]]:=step[t,head[t]]+1;
end;
check(t);
end;
if canmovedown(pre) then
begin
tmp:=movedown(pres,pre);
if not vis(tmp,t) then
begin
inc(tail[t]);
q[t,tail[t]]:=tmp;
step[t,tail[t]]:=step[t,head[t]]+1;
end;
check(t);
end;
end;
Procedure bfs;
begin
head[0]:=0;head[1]:=0;
tail[0]:=1;tail[1]:=1;
step[0,1]:=0;
step[1,1]:=0;
q[0,1]:=st;
q[1,1]:=aim;
while (head[0]<tail[0])and(head[1]<tail[1])do if tail[1]<tail[0] then expand(1) else expand(0);
end;
Begin
init;
bfs;
End.
浙公网安备 33010602011771号