习题:八数码难题(双向BFS)

                                             八数码难题(wikioi1225)

【题目描述】 

  在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

【输入描述】

  输入初试状态,一行九个数字,空格用0表示。

【输出描述】

  只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)。

【样例输入 】

283104765

【样例输出】

4

分析:

  很经典题目,题意不解释了。

  看到最小步骤会想到广搜,本题可以看成是空格移动达到目标状态的问题,记录空格位置和棋盘状态,加入队列不断增加节点入队。同时注意判重,将棋盘转换为九位数(如果第一个数字为0则是八位数),用hash表解决。这样就可以过这道题了。

  但是为了达到更好的时间效率,可以使用双向广搜。

  双向宽搜,顾名思义就是从两边搜,适用于已知起始状态和目标状态,求最短步骤的题目。

  我们可以开两类数组,分别表示正方方向搜索的队列,然后初始,目标状态分别入对应队列,进行扩展节点,直到两个方向搜索相遇时即得出最短步骤。

  出于优化时间的目的,我们往往先搜队列中节点少的方向,轮流进行。

  具体讲就是:两个方向根据队列中节点数交替扩展节点,每扩展一个节点为,在本队列判重后还要在另一个方向的队列中找是否出现,出现说明相遇,输出最短步骤为正方方向扩展到该节点所需最短步骤。

  注意双向广搜时,反方向扩展节点时操作与正方向相反,比如向左移动要变为向右移动,但这点对本题没有影响。

  解释一下

     head,tail表示队头队尾指针,v表示最短步骤,w是棋盘状态,px,py表示空格位置。

     0表示正方向,1表示反方向。彼此可用1-x转换。

    以下就是自己写的了双向广搜+hash判重的八数码AC程序。

代码

program puzzle;
const
  maxn=10007;
type
   hh=^node;
     node=record
       data:longint;
       text:longint;
       next:hh;
   end;
var
  dx:array[1..4]of longint=(0,0,1,-1);
  dy:array[1..4]of longint=(1,-1,0,0);
  hash:array[0..1,0..maxn]of hh;
  head,tail:array[0..1]of longint;
  w:array[0..1,0..362881,1..3,1..3]of longint;
  b:array[1..3,1..3]of longint;
  px,py,v:array[0..1,0..362881]of longint;
  n,i,m,j,x,y,s,h,t,k,r:longint;
  c:char;
function haha(t,x:longint):boolean;
  var i,j,xx:longint;p:hh;
  begin
    xx:=x mod maxn;
    new(p);
    p:=hash[t,xx];
    while p<>nil do
    begin
      if p^.data=x then begin r:=p^.text; exit(true); end;
      p:=p^.next;
    end;
    exit(false);
  end;
procedure put(t,x:longint);
var q:hh;xx:longint;
begin
   xx:=x mod maxn;
   new(q);
   q^.data:=x; q^.text:=tail[t]; q^.next:=hash[t,xx];
   hash[t,xx]:=q;
end;
procedure bfs(x:longint);
var i,j,u,l,ans,xx,yy,g,tmp:longint;
begin
  head[x]:=head[x]+1;
  for i:=1 to 4 do
  begin   b:=w[x,head[x]];
    xx:=px[x,head[x]]+dx[i]; yy:=py[x,head[x]]+dy[i];
    tmp:=b[xx,yy]; 
    b[xx,yy]:=b[px[x,head[x]],py[x,head[x]]];
    b[px[x,head[x]],py[x,head[x]]]:=tmp;
    g:=100000000; ans:=0;
    for u:=1 to 3 do begin
     for l:=1 to 3 do
      begin ans:=ans+g*b[u,l]; g:=g div 10; end;
      end;
      if (xx>0)and(xx<=3)and(yy>0)and(yy<=3)and(haha(x,ans)=false) then
         begin
           tail[x]:=tail[x]+1; px[x,tail[x]]:=xx; py[x,tail[x]]:=yy; 
           w[x,tail[x]]:=b; v[x,tail[x]]:=v[x,head[x]]+1; put(x,ans);
           if haha(1-x,ans)=true then
            begin
              writeln(v[x,tail[x]]+v[1-x,r]); k:=1; break;
            end;//在另一个方向的队列中查找该节点
         end;
        if k=1 then break;
      end;
      if k=1 then exit;
end;
begin
  t:=100000000;
  for i:=1 to 3 do begin
   for j:=1 to 3 do
   begin read(c);if c='0' then begin x:=i; y:=j;end;
   w[1,1,i,j]:=ord(c)-48; s:=s+w[1,1,i,j]*t; t:=t div 10;  end;
   end;
  put(0,123804765); put(1,s); k:=0;//存入两个hash数组
  w[0,1,1,1]:=1; w[0,1,1,2]:=2; w[0,1,1,3]:=3;
  w[0,1,2,1]:=8; w[0,1,2,2]:=0; w[0,1,2,3]:=4;
  w[0,1,3,1]:=7; w[0,1,3,2]:=6; w[0,1,3,3]:=5;
  v[0,1]:=0; v[1,1]:=0; px[0,1]:=2; py[0,1]:=2; px[1,1]:=x; py[1,1]:=y;
  head[0]:=0; head[1]:=0; tail[0]:=1; tail[1]:=1;
  repeat
  if (tail[0]>head[0])and((tail[0]-head[0]<tail[1]-head[1])or(tail[1]<=head[1]))
    then bfs(0) 
    else if tail[1]>head[1] then bfs(1);//交替扩展节点
  if k=1 then break;
  until ((head[0]>=tail[0])or(tail[0]>=362880))and((head[1]>=tail[1])or(tail[1]>=362880));
end.
View Code

 

使用双向广搜,时间效率大大提高,两种方法在不加其它优化的时间效率对比

 

广搜+hash


双向广搜+hash



 

转载于:https://www.cnblogs.com/qtyytq/p/4265757.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八数码问题的解决方法中,C语言可以使用BFS(广度优先搜索)算法来解决。在C语言中,可以通过实现一个EightDigital类来解决八数码问题。通过BFS算法可以找到从初始状态到目标状态的最短路径。在该算法中,通过队列的方式,一层一层地扩展状态空间,直到找到目标状态。 在该实现中,可以使用一个辅助数组来记录每个状态的前驱状态,以便打印出从初始状态到目标状态的路径。具体实现中,可以使用一个stack来存储路径,从目标状态开始,通过查找前驱状态一直到初始状态,将每个状态压入栈中,最后依次弹出栈顶元素即可得到路径。同时,使用一个变量记录步数,即栈的大小,即可得到解的步数。 除了BFS算法外,还可以使用A*算法来解决八数码问题。A*算法是一种启发式搜索算法,通过评估函数来估计从当前状态到目标状态的代价,并选择代价最小的状态进行扩展。在八数码问题中,可以使用曼哈顿距离作为评估函数,即当前状态到目标状态的每个数字所需的水平和垂直移动的总和。通过A*算法可以更快地找到最优解。 总结起来,八数码问题可以使用C语言中的BFS算法和A*算法来解决。BFS算法可以找到最短路径,A*算法可以更快地找到最优解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [c++八数码难题全家桶(A*算法、双向BFSBFS、DFS)](https://blog.csdn.net/qq_54893805/article/details/127440809)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值