八数码问题的两种解法
这个题可以用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.