八数码问题的两种解法

这个题可以用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.


posted @ 2011-10-27 16:12  NoRush  阅读(1852)  评论(0编辑  收藏  举报