zoukankan      html  css  js  c++  java
  • NOIP提高组2013 D2T3 【华容道】

    某王  老师给我们考了一场noip2013的真题。。。心态爆炸!

    题目大意:

    有一个n*m的棋盘,每个格子上都有一个棋子,有些格子上的棋子能够移动(可移动的棋子是固定的),棋盘中有一个格子是空的,仍何可移动的棋子都能够与空格子交换位置。现在有q个询问,每个询问给出kx,ky,sx,sy,ex,ey,分别为空白格子的坐标,要你移动的棋子,目标位置。对于每个询问,要求你求出,把要求你移动的棋子移到目标位置最少移动几次。

    思路分析:

    首先讲一下暴力做法吧。

    这应该大家都能想得到,BFS嘛,记录要求我们移动的棋子(暂且记作1号格子)当前的位置和空白格子(记作2号格子)的位置,当2移动到1旁边时,那么1就可以与2交换位置。

    而正解就是基于暴力展开的。。。

    我们发现其实当2移动到1旁边后,便产生了很多无用的状态,我们只需要知道2在1周围的几个状态就够了,因为1格子进行移动时,无非就是和它周围的几个格子交换位置,而交换位置后2仍然在1的周围,所以我们只需要知道2在从1周围的4个格子走到另一个格子的最小花费即可。

    我们记一个三元组(x,y,k)其中x,y表示1号棋子的坐标,k表示2号棋子相对1号棋子的位置(0表示在1的上面,1表示在1的右边,2表示在1的左边,3表示在1的下面),当然一心想要偷懒的我粗暴地把三元组压成了一个数,是这么压的(x,y,k)=k*n*m+(x-1)*m+y,嗯,免得你们看不懂代码。。。

    那么根据我们先前讲的,设计两种连边:

    1、可以在(x,y,0~3)与(x,y,0~3)之间设计一条长度为空白格子从一个位置(x,y的上下左右)走到另一个位置(x,y的上下左右)的最小花费。

    2、在(x,y,k)与(x+dx[k],y+dy[k],3-k)之间连一条边,其意义便是1号格子与2号格子交换了位置。(现在知道我这么设计方向的(shi)用(wei)心(le)良(tou)苦(lan)了吧)

    先把连边预处理出来,然后我们只要第一次计算出初始位置的2号格子移动到1号格子的上下左右的最小花费(可以BFS暴力做)之后,跑一边Dijkstra就完事儿了!

    代码实现:

    type
      hehe=record
        dist,id:longint;
    end;
      haha=record
        x,y,dist:longint;
    end;
    var
      next,vet,dist:array[1..200000]of longint;
      f:array[1..2000000]of hehe;
      a:array[1..90000]of haha;
      d,fir,visit:array[1..48000]of longint;
      map,vis:array[-50..50,-50..50]of longint;
      dx,dy:array[0..3]of longint;
      sx,sy,ex,ey,kx,ky,xx,yy,n,m,q,i,j,ii,jj,tot,weight,x,y,oo,cost,ans,z,x1,x2,y1,y2:longint;
    procedure add(x,y,z:longint);
    begin
      inc(tot);
      next[tot]:=fir[x];
      fir[x]:=tot;
      vet[tot]:=y;
      dist[tot]:=z;
    end;
    function min(x,y:longint):longint;
    begin
      if x<y then exit(x) else exit(y);
    end;
    procedure swap(x,y:longint);
    var
      t:hehe;
    begin
      t:=f[x]; f[x]:=f[y]; f[y]:=t;
    end;
    procedure up(x:longint);
    begin
      if x=1 then exit;
      if f[x].dist<f[x div 2].dist then begin swap(x,x div 2); up(x div 2); end;
    end;
    procedure down(x:longint);
    var
      k:longint;
    begin
      if x*2>n then exit;
      k:=min(f[x*2].dist,f[x*2+1].dist);
      if f[x].dist>k then
        if k=f[x*2].dist then begin swap(x,x*2); down(x*2); end
        else begin swap(x,x*2+1); down(x*2+1); end;
    end;
    procedure push(x,id:longint);
    begin
      inc(weight);
      f[weight].dist:=x;
      f[weight].id:=id;
      up(weight);
    end;
    procedure pop;
    begin
      x:=f[1].id;
      f[1].dist:=f[weight].dist;
      f[1].id:=f[weight].id;
      f[weight].dist:=oo;
      f[weight].id:=oo;
      dec(weight);
      down(1);
    end;
    procedure dijkstra;
    var
      y,i:longint;
    begin
      fillchar(visit,sizeof(visit),0);
      while weight>0 do
      begin
        pop;
        if x>4800 then continue;
        if d[x]>=oo then break;
        if visit[x]=1 then continue;
        visit[x]:=1;
        i:=fir[x];
        while i<>0 do
        begin
          y:=vet[i];
          if (dist[i]<oo)and(d[y]>d[x]+dist[i]) then
            begin d[y]:=d[x]+dist[i]; push(d[y],y); end;
          i:=next[i];
        end;
      end;
    end;
    function bfs(nx,ny,x1,y1,x2,y2:longint):longint;
    var
      head,tail,dist,xx,yy,x,y,i:longint;
    begin
      if map[x1,y1]+map[x2,y2]<>2 then exit(oo);
      if (x1=x2)and(y1=y2) then exit(0);
      fillchar(vis,sizeof(vis),0);
      head:=0; tail:=1; bfs:=maxlongint;
      a[1].x:=x1; a[1].y:=y1; a[1].dist:=0; vis[x1,y1]:=1;
      while head<>tail do
      begin
        inc(head);
        x:=a[head].x; y:=a[head].y; dist:=a[head].dist;
        for i:=0 to 3 do
        begin
          xx:=x+dx[i]; yy:=y+dy[i];
          if ((xx<>nx)or(yy<>ny))and(map[xx,yy]=1)and(vis[xx,yy]=0) then
          begin
            inc(tail);
            a[tail].x:=xx; a[tail].y:=yy; a[tail].dist:=dist+1;
            vis[xx,yy]:=1;
            if (xx=x2)and(yy=y2) then exit(dist+1);
          end;
        end;
      end;
    end;
    begin
      read(n,m,q);
      for i:=1 to n do
        for j:=1 to m do
          read(map[i,j]);
      dx[0]:=-1; dy[1]:=1; dy[2]:=-1; dx[3]:=1;
      for i:=1 to n do
        for j:=1 to m do
        if map[i,j]=1 then
          for ii:=0 to 3 do
          begin
            x1:=i+dx[ii]; y1:=j+dy[ii]; x2:=i; y2:=j;
            if map[x1,y1]+map[x2,y2]=2 then
            begin
              add(ii*n*m+(x2-1)*m+y2,(3-ii)*n*m+(x1-1)*m+y1,1);
            end;
            for jj:=0 to 3 do
            begin
              x2:=i+dx[jj]; y2:=j+dy[jj];
              if (ii<>jj)and(map[x1,y1]+map[x2,y2]=2) then
              begin
                cost:=bfs(i,j,x1,y1,x2,y2);
                add(ii*n*m+(i-1)*m+j,jj*n*m+(i-1)*m+j,cost);
              end;
            end;
          end;
      while q>0 do
      begin
        read(kx,ky,sx,sy,ex,ey);
        fillchar(d,sizeof(d),$1f);
        oo:=d[1];
        fillchar(f,sizeof(f),$1f);
        weight:=0;
        if (sx=ex)and(sy=ey) then
        begin
          writeln(0);
          dec(q);
          continue;
        end;
        for i:=0 to 3 do
        begin
          x:=sx+dx[i]; y:=sy+dy[i];
          if map[x,y]=1 then
          begin
            d[i*n*m+(sx-1)*m+sy]:=bfs(sx,sy,kx,ky,x,y);
            push(d[i*n*m+(sx-1)*m+sy],i*n*m+(sx-1)*m+sy);
          end;
        end;
        dijkstra;
        ans:=oo;
        for i:=0 to 3 do
        begin
          z:=i*n*m+(ex-1)*m+ey;
          if d[z]<=ans then ans:=d[z];
        end;
        if ans=oo then writeln(-1) else writeln(ans);
        dec(q);
      end;
    end.
  • 相关阅读:
    linux设备驱动第五篇:驱动中的并发与竟态
    chromium浏览器开发系列第二篇:如何编译最新chromium源码
    你所不知道的html5与html中的那些事(二)
    vim 高级使用技巧第二篇
    FFMPEG高级编程第一篇:环境搭建及编译
    android apk 防止反编译技术第一篇-加壳技术
    你所不知道的html5与html中的那些事(一)
    交通视频
    Git命令----放弃本地修改使用服务器上的代码
    IE10(去掉文本框的X)
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/9818592.html
Copyright © 2011-2022 走看看