码迷,mamicode.com
首页 > 其他好文 > 详细

NOIP2013华容道——史上最强题解

时间:2015-08-07 16:12:22      阅读:4655      评论:0      收藏:0      [点我收藏+]

标签:搜索   数据   

技术分享
技术分享
技术分享
[解题思路]
解题思路
这道题的数据范围是n≤30,所以,我们可以看到,裸的O(n4) 的宽搜对于求解q较小的情况是无压力的,但是在q大的情况下,毫无疑问会时间超限,明显,在q较大的情况下,我们需要将每次宽搜中重复搜索的冗余信息除去,所以我们可以先分析题目性质:(这里称要移动的棋子为目标棋子)
首先,如果要移动目标棋子,那么我们首先必须要将空格移到该棋子的上下左右四个方
向上相邻位置之一,然后才可以移动该棋子。 然后,我们分析该棋子移动时候的性质: 棋子每次可以移动,仅当空格位于其相邻位置的时候,每次移动完棋子,空格总会在棋
子相邻的位置,那么我们发现,对于棋子在某一位置,然后空格又在其四个方向上某一
相邻位置时,棋子要想某一方向移动一个时的花费的步数是一定的,那么,就可以先进
行一次预处理,预处理出对于目标棋子在上述条件下每次移动所需的步数。然后,预处理完成之后,我们会发现每次查询都会变成一个求最短路的问题,用Dijstra或SPFA的话,可以在时限范围内解决。(SPFA较好)
实现:
定义一个数组f[x][y][k][h],表示目标棋子在位置(x,y)且空格在目标棋子的k方向上的相邻格子时,目标棋子往h方向移动1格所需的步数,然后用状态[x][y][k]作为节点建图,用各个状态的关系连边,每次询问时重新定义一个源点跟终点,跑最短路就可以得出答案。
(预处理时跑n2次O(n2)的BFS就可以了)
[参考程序]

const  u:array[1..4] of integer=(-1,0,1,0);   //四种移法
      v:array[1..4] of integer=(0,1,0,-1);
      oo=100000000;
type node=record
      x,y,cs,l:longint;
end;
var ft:text;
    n,m,q,ii,i,i1,j1,j,l,w,x,y,x1,y1,x2,y2,x3,y3,es,ey,wei,tou,kx,ky,ans:longint;
    r:array[0..5000000] of node;
    aa,a,fb,fb2:array[0..31,0..31] of longint;
    ff,z,z2:array[0..31,0..31,1..4] of longint;
    f:array[0..31,0..31,1..4,1..4] of longint;
procedure bfs(x,y:longint);  //宽搜
var i,x1,y1:longint;
begin
  wei:=1;
  tou:=0;
  r[wei].x:=x; //r.x用来记录可以移动的横坐标
  r[wei].y:=y; //纵坐标
  r[wei].cs:=fb[x,y]; //几步可以移到
  while tou<wei do
   begin
    inc(tou);
    for i:=1 to 4 do //是否可以再向别的点移动
     begin
      x1:=r[tou].x+u[i];
      y1:=r[tou].y+v[i];
      if (a[x1,y1]=1)and(r[tou].cs+1<fb[x1,y1]) then //可以移到并且比之前的步数少
        begin
         inc(wei);
         r[wei].x:=x1;
         r[wei].y:=y1;
         r[wei].cs:=r[tou].cs+1;
         fb[x1,y1]:=r[wei].cs; //记录(等过程结束移到f四维数组里)
        end;
     end;
   end;
end;
begin
  readln(n,m,q);     //读入方阵
  for i:=1 to n do
    begin
    for j:=1 to m do
      read(a[i,j]);
    readln;
    end;
  aa:=a;
  for i:=1 to n do //初始化
   for j:=1 to m do
     begin
      fb2[i,j]:=oo;
      for l:=1 to 4 do
        begin
         z2[i,j,l]:=oo;
         for w:=1 to 4 do f[i,j,l,w]:=oo;
        end;
     end;
  for i:=1 to n do  //预处理
    for j:=1 to m do
     if a[i,j]=1 then  //如果该棋子可以移动
       for l:=1 to 4 do  //四个方向
         begin
          x:=i+u[l];
          y:=j+v[l];
          if (x>0)and(x<n+1)and(y>0)and(y<m+1)and(a[x,y]=1) then //假设四周有空格
            begin
             a[x,y]:=0;  //已被移动过,防止重复移动
             fb:=fb2;    //重置
             fb[i,j]:=1;  //空格移到这个位置
             bfs(i,j);
             for w:=1 to 4 do
               begin
                x1:=x+u[w];
                y1:=y+v[w];
                if (x1>0)and(x1<n+1)and(y1>0)and(y1<m+1)and(fb[x1,y1]<oo) then
                  begin
                  f[i,j,l,w]:=fb[x1,y1];  //记录表示目标棋子在位置(x,y)且空格在目标棋子的k方向上的相邻格子时,目标棋子往h方向移动1格所需的步数
                  end;
               end;
             a[x,y]:=1; //回溯
            end;
         end;
  for ii:=1 to q do
    begin
    read(kx,ky,x,y,es,ey);
    if (x=es)and(y=ey) then //如果初始位置与目标位置相同
      begin
       writeln(0);
       continue;
      end;
    fb:=fb2;  //
    a[x,y]:=0; //空格不能移到这个点
    fb[kx,ky]:=0; //空格移动0步到
    bfs(kx,ky);  //空格能到的地方
    wei:=0;
    z:=z2;  //重置
    fillchar(ff,sizeof(ff),0);
    for j:=1 to 4 do //目标棋子向四周移动
      begin
       x1:=x+u[j];
       y1:=y+v[j];
       if (x1>0)and(x1<n+1)and(y1>0)and(y1<m+1)and(fb[x1,y1]<oo) then //空格可以移到目标棋子所在格子
         begin  //记录
          inc(wei);
          r[wei].x:=x;
          r[wei].y:=y;
          r[wei].l:=j;  //表示方向
          z[x,y,j]:=fb[x1,y1]; //某位置向某方向最多可以移动几步
          ff[x,y,j]:=1;  //某位置向某方向可以移动
         end;
      end;
    tou:=0;
    while tou<wei do //SPFA
      begin
       inc(tou);
       for i:=1 to 4 do
        begin
         x2:=r[tou].x;
         y2:=r[tou].y;
         x1:=x2+u[r[tou].l]+u[i];
         y1:=y2+v[r[tou].l]+v[i];
         x3:=x2+u[r[tou].l];
         y3:=y2+v[r[tou].l];
        if (f[x2,y2,r[tou].l,i]<>oo)and(z[x3,y3,i]>z[x2,y2,r[tou].l]+f[x2,y2,r[tou].l,i]) then 
begin
            z[x3,y3,i]:=z[x2,y2,r[tou].l]+f[x2,y2,r[tou].l,i];
            if ff[x3,y3,i]=0 then //记录
              begin
              ff[x3,y3,i]:=1;
              inc(wei);
              r[wei].x:=x3;
              r[wei].y:=y3;
              r[wei].l:=i;
              end;
           end;
         end;
       ff[r[tou].x,r[tou].y,r[tou].l]:=0;
      end;
    ans:=oo;
    for i:=1 to 4 do 查找
    if z[es,ey,i]<ans then ans:=z[es,ey,i];
if ans=oo then writeln(-1) 
else writeln(ans);
    a[x,y]:=1; //回溯
  end;
end.

版权声明:本文为博主原创文章,未经博主允许不得转载。

NOIP2013华容道——史上最强题解

标签:搜索   数据   

原文地址:http://blog.csdn.net/yangs_s/article/details/47339861

(3)
(2)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!