题解 P2864 【[USACO06JAN]树林The Grove】

· · 题解

为什么每人发题解?

来发Pascal

当然这是BFS的题。但是怎么BFS呢?我们要绕这个树林走一圈,而裸的BFS实现不了。怎么办?

都说了只是裸的BFS过不了而已。现在来讲正解:

我们随便挑一棵树,在它的右边往下建一堵“墙”。比如样例,如下图所示:

棕色的表示选的那棵树,在他右边往下面建一堵墙。

建了这堵墙以后,BFS时我们就强制让它不穿过这堵墙。

我们在BFS时,用a[i,j]表示从起点出发(不穿过墙)的最短路径(最少步数)。

比如样例(墙就建在那个地方),a数组的值应该是:

  8  7  6  5  5  5  5
  8  7  6 -1  4  4  4
  8  7 -1 -1 -1  3  3
  8  8  8 -1 -1 -1  2
  9  9  9 -1  2  1  1
 10 10 10  3  2  1  0

最后我们再把墙拆掉,两边能连起来的最小的和就是答案。具体见程序

献上我那丑陋的代码:

uses math;
const //坐标增量,八个方向
  dx:array[1..8]of -1..1 = (-1,0,1,0,-1,-1,1,1);
  dy:array[1..8]of -1..1 = (0,-1,0,1,-1,1,-1,1);
var
  n,m,i,j,sx,sy,zx,zy,h,t,ans:longint; //sx,sy是起点,zx,zy是“墙”的位置
  b:array[0..51,0..51]of boolean;
  a:array[0..51,0..51]of longint;
  q:array[0..10001,1..2]of longint;
  c:char;
procedure bfs(x,y:longint);
var
  k,i,j:longint;
begin
  h:=0; t:=1; q[t,1]:=x; q[t,2]:=y; b[x,y]:=false; a[x,y]:=0;
  while h<t do 
    begin
      inc(h); x:=q[h,1]; y:=q[h,2];
      if (x>=zx) and (y=zy) then continue; //如果在墙相邻的左边,这个值就不应该扩展
      for k:=1 to 8 do 
        begin
          i:=x+dx[k]; j:=y+dy[k];
          if (i>0) and (i<=n) and (j>0) and (j<=m) and b[i,j] then 
            begin
              if (i>=zx) and (j=zy) and (dy[k]=-1) then continue; //墙相邻的右边其实不能过来
              a[i,j]:=a[x,y]+1; b[i,j]:=false;
              inc(t); q[t,1]:=i; q[t,2]:=j;
            end;
        end;
    end;
end;
begin
  randomize;
  readln(n,m);
  for i:=0 to n+1 do 
    for j:=0 to m+1 do 
      a[i,j]:=-1; //全部置为-1,处理边界问题和树林
  for i:=1 to n do 
    begin
      for j:=1 to m do 
        begin
          read(c);
          if c='*' then begin sx:=i; sy:=j; end;
          if c='X' then b[i,j]:=false else b[i,j]:=true;
        end;
      readln;
    end;
  repeat zx:=random(n)+1; zy:=random(m)+1; until not b[zx,zy];
  bfs(sx,sy);
  writeln(zx,' ',zy);
  for i:=1 to n do 
    begin
      for j:=1 to m do write(a[i,j]:3);
      writeln;
    end;
  ans:=maxlongint shr 1;
  for i:=zx+1 to n do 
    if a[i,zy]>=0 then //不是树林
      for j:=-1 to 1 do 
        if a[i+j,zy+1]>=0 then ans:=min(ans,a[i,zy]+a[i+j,zy+1]); //不是树林也没有超出(超出自动是-1,上面说过了),比较出较小值
  write(ans+1); //起点本身加上去
end.