[PKU 1077] 搜索之BFS & A*(上)

{

最近在搞搜索

学习了一些技巧

还重写了以前一个8数码问题

写了一个裸搜 一个双向宽搜 还有一个A*

这一部分先讨论BFS的内容

}

原题 http://acm.pku.edu.cn/JudgeOnline/problem?id=1077

题意 8数码问题 很经典

  读入是一行一个串 忽略空格

               1 2 3

  1 2 3 x 4 6 7 5 8  <=>  x 4 6

               7 5 8

  输出 一行一个串 表示x移动的最短过程{'u','d','l','r'}

很显然用裸的BFS可以解决问题

数据结构

  队列(BFS的需要)

  Hash表(+BKDRhash算法)

算法

  裸搜的具体内容 不细讲

  *状态的存储使用的是string[9]类型 再记录一下x的位置

  *重点是判重 我们使用Hash表在O(1)的复杂度内判重

    Hash表存的是节点在队列里的位置

    hash算法使用的是BKDRhash http://www.nocow.cn/index.php/BKDRHash

    这个字符串hash方法很优秀 也很好实现 复杂度为O(L)

    用 and $FFFFF 代替mod操作可以快很多倍

  *具体实现还要注意输出和判无解

    输出答案时候的顺序 还有四个方向不要搞错了

    我是记录前驱然后 递归输出的

    判断无解----baidu一下 你知道的太多了

    先判无解可以防止在无解的数据上耗时过多

    因为此时如果要搜索必须搜遍整棵搜索树

    不过362880个状态不会死惨的

  *细节方面

    不要忘了把q[1]放到hash里去

    挪动x要注意条件 也就是for循环开头的两个if

贴个裸搜的代码 常数不大 才200+ms

对于一个裸搜来说 还是很不错的

7583676 Master_Chivu 1077 Accepted 10356K 282MS Pascal 2198B 2010-09-06 22:53:00

 

  
    
1 { $I+,Q+,R+,S+ }
2   const maxq = 200000 ;
3 base = $FFFFF;
4 goal = ' 12345678x ' ;
5 list: set of char = [ ' 1 ' .. ' 8 ' , ' x ' ];
6 dx: array [ 1 .. 4 ] of longint = ( 1 , 3 , - 1 , - 3 );
7 d: array [ 1 .. 4 ] of char = ( ' r ' , ' d ' , ' l ' , ' u ' );
8   var q: array [ 1 ..maxq] of string[ 9 ];
9 blank,prev, { dep, } ans: array [ 1 ..maxq] of longint;
10 hash: array [ 0 ..base] of longint;
11 tab,next: array [ 1 ..maxq] of longint;
12 n,i,j,t,h,tt,p,ans0,tx:longint;
13 flag:boolean;
14 ch:char;
15   procedure out(x:longint);
16   begin
17   if x = 0 then exit;
18 out(prev[x]);
19   if ans[x] <> 0 then write(d[ans[x]]);
20   { writeln(copy(q[x],1,3));
21 writeln(copy(q[x],4,3));
22 writeln(copy(q[x],7,3));
23 writeln; }
24   end ;
25 begin
26 assign(input, ' eight.in ' ); reset(input);
27 assign(output, ' eight.out ' ); rewrite(output);
28 q[ 1 ]: = '' ; i: = 0 ; n: = 9 ;
29 while not eoln do
30 begin
31 read(ch);
32 if not (ch in list) then continue;
33 inc(i);
34 q[ 1 ]: = q[ 1 ] + ch;
35 if ch = ' x ' then blank[ 1 ]: = i;
36 end ;
37 readln;
38 t: = 0 ;
39 for i: = 1 to n do
40 for j: = 1 to i - 1 do
41 if (q[ 1 ][i] <> ' x ' ) and (q[ 1 ][j] <> ' x ' ) and (q[ 1 ][j] < q[ 1 ][i])
42 then inc(t);
43 if odd(t)
44 then begin
45 writeln( ' unsolvable ' );
46 close(input); close(output);
47 halt;
48 end ;
49 tt: = 0 ;
50 prev[ 1 ]: = 0 ; ans[ 1 ]: = 0 ; // dep[ 1 ]: = 0 ;
51 h: = 1 ; t: = 1 ;
52 ans0: = 0 ;
53 for j: = 1 to n do
54 ans0: = (ans0 * 131 + ord(q[t][j])) and base;
55 inc(tt); tab[tt]: = t;
56 next[tt]: = hash[ans0]; hash[ans0]: = tt;
57 while h <= t do
58 begin
59 for i: = 1 to 4 do
60 begin
61 if ((blank[h] = 4 ) or (blank[h] = 7 )) and (i = 3 )
62 then continue;
63 if ((blank[h] = 3 ) or (blank[h] = 6 )) and (i = 1 )
64 then continue;
65 tx: = blank[h] + dx[i];
66 if (tx >= 1 ) and (tx <= n)
67 then begin
68 inc(t);
69 q[t]: = q[h];
70 q[t][blank[h]]: = q[h][tx];
71 q[t][tx]: = q[h][blank[h]];
72 if q[t] = goal
73 then begin
74 // writeln(dep[h] + 1 );
75 prev[t]: = h; ans[t]: = i; out(t);
76 writeln;
77 close(input); close(output);
78 halt;
79 end ;
80 flag: = false;
81 ans0: = 0 ;
82 for j: = 1 to n do
83 ans0: = (ans0 * 131 + ord(q[t][j])) and base;
84 p: = hash[ans0];
85 while p <> 0 do
86 begin
87 if q[tab[p]] = q[t] then begin flag: = true; break; end ;
88 p: = next[p];
89 end ;
90 if flag
91 then dec(t)
92 else begin
93 prev[t]: = h;
94 // dep[t]: = dep[h] + 1 ;
95 blank[t]: = tx;
96 ans[t]: = i;
97 inc(tt); tab[tt]: = t;
98 next[tt]: = hash[ans0]; hash[ans0]: = tt;
99 end ;
100 end ;
101 end ;
102 inc(h);
103 end ;
104 end .
105

接下来我们考虑进一步的优化

 

也就是双向宽搜

我记得我小时候玩走迷宫是从出口开始走的-.-

其实从出口开始走和从入口开始时一样的==|||

当时应该一个手指头从入口开始 一个从出口开始 知道碰面为止才好^.^

不过当时还是个小p孩 是不知道搜索为何物滴*.*

扯远了

双向宽搜适用的条件有2个

  一是目标状态已知

  二是状态是可以还原

比如这个8数码问题目标状态很显然已知:12345678x

还原一个状态就是倒着走 其实也是移动x的位置 也是可行的

所以双向宽搜适用

在这里使用双向宽搜时 要注意以下几点

  *建议使用一个二维数组q[1..maxq][1..2]保存两个队列

  (尽管我没有这么做...)这样做的好处是再后面讲Hash判重会具体说明

  *要比裸BFS多记录一个dep 每次选择dep浅的一端扩展

  *所有状态存在一个Hash表里 用kind[]来表示是头一端的节点还是尾一端的节点

  kind[]存1和2 若为1则在头端 若为2则在尾端

  此时用数组的第二维可以代替if语句 缩减代码

  *如果碰到头端扩展出在尾端出现过的节点或是在尾端扩展出头端出现过的节点

  说明已经找到一条最短的连接目标与起始的节点 需要输出

  同样是递归输出 细节更加需要注意 因为这一次的解是两段拼接起来的

  用outx(longint)输出前段 outy(longint)输出后一段

  不要输重复了 也不要输漏了 注意答案的顺序 前后的递归过程有关键的不同

我的代码比较乱 没用什么函数 全都坨在main里了

速度还可以 小号交了16ms 大号32ms RP有问题 PKU测时测内存都有大问题

7584216 Master_Chivu 1077 Accepted 5652K 32MS Pascal 4072B 2010-09-07 01:07:42
  
    
1 { $I+,Q+,R+,S+ }
2 const maxq = 100000 ;
3 base = $FFFFF;
4 goal = ' 12345678x ' ;
5 list: set of char = [ ' 1 ' .. ' 8 ' , ' x ' ];
6 dx: array [ 1 .. 4 ] of longint = ( 1 , 3 , - 1 , - 3 );
7 d: array [ 1 .. 4 ] of char = ( ' r ' , ' d ' , ' l ' , ' u ' );
8 d2: array [ 1 .. 4 ] of char = ( ' l ' , ' u ' , ' r ' , ' d ' );
9 var qx,qy: array [ 1 ..maxq] of string[ 9 ];
10 bx,by,prevx,prevy,depx,depy,ansx,ansy: array [ 1 ..maxq] of longint;
11 hash: array [ 0 ..base] of longint;
12 kind,tab,next: array [ 1 ..maxq shl 1 ] of longint;
13 p,n,i,j,ans0,tt,t,tx,ty,hx,hy,tryx,mark:longint;
14 flag:boolean;
15 ch:char;
16 procedure outx(x:longint);
17 begin
18 if x = 0 then exit;
19 outx(prevx[x]);
20 if ansx[x] <> 0 then write(d[ansx[x]]);
21 { writeln(copy(qx[x],1,3));
22 writeln(copy(qx[x],4,3));
23 writeln(copy(qx[x],7,3));
24 writeln; }
25 end ;
26 procedure outy(x:longint);
27 begin
28 if x = 0 then exit;
29 { writeln(copy(qy[x],1,3));
30 writeln(copy(qy[x],4,3));
31 writeln(copy(qy[x],7,3));
32 writeln; }
33 if ansy[x] <> 0 then write(d2[ansy[x]]);
34 outy(prevy[x]);
35 end ;
36 begin
37 assign(input, ' eight.in ' ); reset(input);
38 assign(output, ' eight.out ' ); rewrite(output);
39 qx[ 1 ]: = '' ; i: = 0 ; n: = 9 ;
40 while not eoln do
41 begin
42 read(ch);
43 if not (ch in list) then continue;
44 inc(i);
45 qx[ 1 ]: = qx[ 1 ] + ch;
46 if ch = ' x ' then bx[ 1 ]: = i;
47 end ;
48 t: = 0 ;
49 for i: = 1 to n do
50 for j: = 1 to i - 1 do
51 if (qx[ 1 ][i] <> ' x ' ) and (qx[ 1 ][j] <> ' x ' ) and (qx[ 1 ][j] < qx[ 1 ][i])
52 then inc(t);
53 if odd(t)
54 then begin
55 writeln( ' unsolvable ' );
56 close(input); close(output);
57 halt;
58 end ;
59 qy[ 1 ]: = goal; by[ 1 ]: = 9 ;
60 ans0: = 0 ;
61 for j: = 1 to n do
62 ans0: = (ans0 * 131 + ord(qx[ 1 ][j])) and base;
63 inc(tt); tab[tt]: = 1 ; kind[tt]: = 1 ;
64 next[tt]: = hash[ans0]; hash[ans0]: = tt;
65 ans0: = 0 ;
66 for j: = 1 to n do
67 ans0: = (ans0 * 131 + ord(qy[ 1 ][j])) and base;
68 inc(tt); tab[tt]: = 1 ; kind[tt]: = 2 ;
69 next[tt]: = hash[ans0]; hash[ans0]: = tt;
70 hx: = 1 ; hy: = 1 ; tx: = 1 ; ty: = 1 ;
71 prevx[ 1 ]: = 0 ; prevy[ 1 ]: = 0 ;
72 depx[ 1 ]: = 0 ; depy[ 1 ]: = 0 ;
73 while (hx <= tx) and (hy <= ty) do
74 begin
75 if depx[tx] <= depy[ty]
76 then begin
77 for i: = 1 to 4 do
78 begin
79 if ((bx[hx] = 4 ) or (bx[hx] = 7 )) and (i = 3 )
80 then continue;
81 if ((bx[hx] = 3 ) or (bx[hx] = 6 )) and (i = 1 )
82 then continue;
83 tryx: = bx[hx] + dx[i];
84 if (tryx >= 1 ) and (tryx <= n)
85 then begin
86 inc(tx);
87 qx[tx]: = qx[hx];
88 qx[tx][bx[hx]]: = qx[hx][tryx];
89 qx[tx][tryx]: = qx[hx][bx[hx]];
90 flag: = false;
91 ans0: = 0 ;
92 for j: = 1 to n do
93 ans0: = (ans0 * 131 + ord(qx[tx][j])) and base;
94 p: = hash[ans0];
95 while p <> 0 do
96 begin
97 if (kind[p] = 1 ) and (qx[tab[p]] = qx[tx])
98 or (kind[p] = 2 ) and (qy[tab[p]] = qx[tx])
99 then begin flag: = true; mark: = p; break; end ;
100 p: = next[p];
101 end ;
102 if flag
103 then begin
104 if kind[mark] = 2
105 then begin
106 // writeln(depx[hx] + 1 + depy[tab[mark]]);
107 prevx[tx]: = hx; ansx[tx]: = i;
108 outx(tx);
109 outy( { prevy[ } tab[mark] { ] } );
110 writeln;
111 close(input); close(output);
112 halt;
113 end ;
114 dec(tx);
115 end
116 else begin
117 prevx[tx]: = hx;
118 depx[tx]: = depx[hx] + 1 ;
119 bx[tx]: = tryx;
120 ansx[tx]: = i;
121 inc(tt); tab[tt]: = tx; kind[tt]: = 1 ;
122 next[tt]: = hash[ans0]; hash[ans0]: = tt;
123 end ;
124 end ;
125 end ;
126 inc(hx);
127 end
128 else begin
129 for i: = 1 to 4 do
130 begin
131 if ((by[hy] = 4 ) or (by[hy] = 7 )) and (i = 3 )
132 then continue;
133 if ((by[hy] = 3 ) or (by[hy] = 6 )) and (i = 1 )
134 then continue;
135 tryx: = by[hy] + dx[i];
136 if (tryx >= 1 ) and (tryx <= n)
137 then begin
138 inc(ty);
139 qy[ty]: = qy[hy];
140 qy[ty][by[hy]]: = qy[hy][tryx];
141 qy[ty][tryx]: = qy[hy][by[hy]];
142 flag: = false;
143 ans0: = 0 ;
144 for j: = 1 to n do
145 ans0: = (ans0 * 131 + ord(qy[ty][j])) and base;
146 p: = hash[ans0];
147 while p <> 0 do
148 begin
149 if (kind[p] = 1 ) and (qx[tab[p]] = qy[ty])
150 or (kind[p] = 2 ) and (qy[tab[p]] = qy[ty])
151 then begin flag: = true; mark: = p; break; end ;
152 p: = next[p];
153 end ;
154 if flag
155 then begin
156 if kind[mark] = 1
157 then begin
158 // writeln(depy[hy] + 1 + depx[tab[mark]]);
159 prevy[ty]: = hy; ansy[ty]: = i;
160 outx(tab[mark]);
161 outy(ty);
162 writeln;
163 close(input); close(output);
164 halt;
165 end ;
166 dec(ty);
167 end
168 else begin
169 prevy[ty]: = hy;
170 depy[ty]: = depy[hy] + 1 ;
171 by[ty]: = tryx;
172 ansy[ty]: = i;
173 inc(tt); tab[tt]: = ty; kind[tt]: = 2 ;
174 next[tt]: = hash[ans0]; hash[ans0]: = tt;
175 end ;
176 end ;
177 end ;
178 inc(hy);
179 end ;
180 end ;
181 end .
182

 

我们在下半部分里讨论这个问题的A*算法 A*算法会更加牛B 0ms可以解决问题

 

BOB HAN原创 转载请注明出处

你可能感兴趣的:(bfs)