zoukankan      html  css  js  c++  java
  • 2018/7/16 YMOI模拟 NOIP2013D2T3华容道

    题目描述 Description

    小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
    小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

    1. 在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
    2. 有些棋子是固定的,有些棋子则是可以移动的;
    3. 任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

    给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列,目标位置为第 TX_i 行第 TY_i 列。
    假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

    输入描述 Input Description

    第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
    接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
    接下来的 q 行,每行包含 6 个整数依次是 EX_i、EY_i、SX_i、SY_i、TX_i、TY_i,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

    输出描述 Output Description

    输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出-1。

    样例输入 Sample Input

    3 4 2 
    0 1 1 1 
    0 1 1 0 
    0 1 0 0 
    3 2 1 2 2 2 
    1 2 2 2 3 2

    样例输出 Sample Output


    -1

    数据范围及提示 Data Size & Hint

    【样例说明】
    棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
    第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。
    移动过程如下:

    第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。

    要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。

    【数据范围】
    对于 30%的数据,1 ≤ n, m ≤ 10,q = 1; 
    对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10; 
    对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。

    ——————————————————————————————————————————————————————————————————————————

    思路一(虽然没错但是会TLE)

    一看到题目,联想到以前做过的八数码。于是可以想到把空白块当作可以自由移动的单位进行BFS。由于想不到什么优化措施,所以就敲了一段大爆搜……虽然TLE是意料之中,但是没想到竟然能拿到70分(那这就说明我离正解不远了/误)

    蒟蒻的大爆搜就别看了qwq

    ——————————————————————————————————————————————————————————————————————————

    思路二(正解)

    整理一下为什么思路一的BFS是错的。

    在一个规模较小的数据范围内,思路一的BFS是可以在1s内得出结论的……但是很不幸,虽然出题人很善良的给了70分(是不是没卡掉),但是爆搜还是没有前途的;

    思路一的BFS缺陷就是,白块是以一种玄妙不可预测的路线行进的(误)。在搜索的时候会浪费很多时间在根本不可能的情况上(不做无法实现的梦)。

    所以在冥(ming)思(le)苦(ti)想(jie)以后想到了绝妙的算法!

    对于最优的操作,有一个前提是空白块一定要先移动到钦点块的旁边。然后空白块和钦点块再作为一个整体移动。

    空白块和钦点块的移动可以用SPFA来求最短路径。建图的方法就是把空白块和钦点块的位置的状态看作一个点,再把每个状态之间互相转换的过程看作边,在这个图里跑一边SPFA就可以轻松求出正解!

    以下是蒟蒻敲了一个下午的代码

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<queue>
      4 #include<cstring>
      5 using namespace std;
      6 #define MAXN 31
      7 #define MAXM 40001
      8 #define INF 0x3f3f3f3f
      9 int n,m,q,num[MAXN][MAXN][5],tot,cnt,head[MAXM],vis[MAXN][MAXN],ex,ey,sx,sy,tx,ty,dis[MAXM],used[MAXM],mp[MAXN][MAXN];
     10 const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
     11 struct Edge
     12 {
     13     int to,dis,next;
     14 }e[MAXM];
     15 struct ZT{int x,y,steps;};
     16 void add(int from,int to,int dis)
     17 {
     18     e[++cnt].next=head[from];
     19     e[cnt].to=to;
     20     e[cnt].dis=dis;
     21     head[from]=cnt;
     22 }
     23 int bfs(int ax,int ay,int bx,int by,int cx,int cy)
     24 {
     25     if(ax==bx&&ay==by) return 0;
     26     memset(vis,0,sizeof(vis));
     27     queue<ZT>Q;
     28     while(!Q.empty()) Q.pop();
     29     Q.push((ZT){ax,ay,0});
     30     vis[ax][ay]=vis[cx][cy]=1;
     31     while(!Q.empty())
     32     {
     33         ZT u=Q.front();Q.pop();
     34         if(u.x==bx&&u.y==by) return u.steps;
     35         for(int i=0;i<4;i++)
     36         {
     37             int x=u.x+dx[i],y=u.y+dy[i];
     38             if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]&&!vis[x][y])
     39             {
     40                 Q.push((ZT){x,y,u.steps+1});
     41                 vis[x][y]=1;
     42                 if(x==bx&&y==by) return u.steps+1;
     43             }
     44         }
     45     }
     46     return INF;
     47 }
     48 void init()
     49 {
     50     for(int i=1;i<=n;i++)
     51         for(int j=1;j<=m;j++)
     52             for(int k=0;k<4;k++)
     53                 if(mp[i][j]&&mp[i+dx[k]][j+dy[k]])
     54                     num[i][j][k]=++tot;
     55     for(int i=1;i<=n;i++)
     56         for(int j=1;j<=m;j++)
     57             for(int k=0;k<4;k++)
     58                 if(num[i][j][k])
     59                     add(num[i][j][k],num[i+dx[k]][j+dy[k]][k^1],1);
     60     for(int i=1;i<=n;i++)
     61         for(int j=1;j<=m;j++)
     62             for(int k=0;k<4;k++)
     63                 for(int l=0;l<4;l++)
     64                     if(l!=k&&num[i][j][k]&&num[i][j][l])
     65                         add(num[i][j][k],num[i][j][l],bfs(i+dx[k],j+dy[k],i+dx[l],j+dy[l],i,j));
     66 }
     67 int spfa()
     68 {
     69     queue<int>Q;
     70     if(sx==tx&&sy==ty) return 0;
     71     for(int i=1;i<=tot;i++) dis[i]=INF;
     72     for(int k=0;k<4;k++)
     73     {
     74         if(num[sx][sy][k])
     75         {
     76             dis[num[sx][sy][k]]=bfs(ex,ey,sx+dx[k],sy+dy[k],sx,sy);
     77             Q.push(num[sx][sy][k]);
     78         }
     79     }
     80     while(!Q.empty())
     81     {
     82         int u=Q.front();Q.pop();
     83         used[u]=0;
     84         for(int i=head[u];i;i=e[i].next)
     85         {
     86             int v=e[i].to;
     87             if(dis[v]>dis[u]+e[i].dis)
     88             {
     89                 dis[v]=dis[u]+e[i].dis;
     90                 if(!used[v])
     91                 {
     92                     Q.push(v);
     93                     used[v]=1;
     94                 }
     95             }
     96         }
     97     }
     98     int ans=INF;
     99     for(int k=0;k<4;k++)
    100     {
    101         if(num[tx][ty][k]) ans=min(ans,dis[num[tx][ty][k]]);
    102     }
    103     return ans==INF?-1:ans;
    104 }
    105 int main()
    106 {
    107     scanf("%d%d%d",&n,&m,&q);
    108     for(int i=1;i<=n;i++)
    109         for(int j=1;j<=m;j++)
    110             scanf("%d",&mp[i][j]);
    111     init();
    112     while(q--)
    113     {
    114         scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
    115         printf("%d
    ",spfa());
    116     }
    117     return 0;
    118 }
    118行orz

    这道题做的我要死了orz

    顺便吐槽一下为什么NOIP2013D2前两道都是普及的水题,到最后一道题就这么难orz

  • 相关阅读:
    BZOJ1511: [POI2006]OKR-Periods of Words
    BZOJ1009: [HNOI2008]GT考试
    BZOJ1355: [Baltic2009]Radio Transmission
    BZOJ1415: [Noi2005]聪聪和可可
    BZOJ1004: [HNOI2008]Cards
    UVA11077 Find the Permutations
    LA3641 Leonardo's Notebook
    UVA10294 Arif in Dhaka
    UVA11762 Race to 1
    UVA11427 Expect the Expected
  • 原文地址:https://www.cnblogs.com/fl0w3r/p/9320625.html
Copyright © 2011-2022 走看看