zoukankan      html  css  js  c++  java
  • NOIP2013 华容道

     

    3.华容道

    (puzzle.cpp/c/pas)

    【问题描述】

    小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。

    小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

    1.   在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;

    2.   有些棋子是固定的,有些棋子则是可以移动的;

    3.   任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

    给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EXi 行第 EYi 列,指定的可移动棋子的初始位置为第 SXi 行第 SYi 列,目标位置为第 TXi 行第 TYi 列。

    假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

    【输入】

    输入文件为 puzzle.in。

    第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;

    接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。

    接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

    【输出】

    输出文件名为 puzzle.out。

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

    【输入输出样例】

    puzzle.in

    puzzle.out

    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

    2

    -1

    【输入输出样例说明】

    棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

    1. 第一次游戏,空白格子的初始位置是  (3,  2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(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。

    【思路】

      BFS+SPFA

      以下摘自洛谷【题解】
       考验状态记录技巧的一道好题!因为我们只要考虑指定块的位置,而指定块位置的移动和空白块有关,我们可以记录(x1,y1,x2,y2)表示指定块在(x1,y1),空白块在(x2,y2)的状态。由于空白块可以四方向移动,所以每个状态会向四个状态连边。这样共有(nm)^2个状态,总复杂度为O(q(nm)^2),只能通过60%的数据。
    但是我们可以发现:只有空白块位于指定块的四方向上,指定块才可以移动。所以,我们可以记(x1,y1,dir)表示指定块在(x1,y1),空白块在指定块的dir方向(0表示上,1表示下什么的......)的状态。这样状态只有4nm个。
    接下来我们考虑各个状态之间的连边。首先,空白块和指定块可以交换位置,这两个状态连边的边权为1;其次,假定空白块在指定块上方,空白块可以通过若干步移动来到空白块下/左/右方。这些状态连边的边权我们可以通过BFS计算出来。
    这样就构造出了一张图,先把空白块移动到目标块旁边,之后向目标状态(空白块可以位于指定块的四个方向)做最短路即可。用spfa复杂度为O(qknm),可以通过100%的数据。

     

    【代码】

     1 #include <stdio.h>
     2 #include <string.h>
     3 #define QLEN 4000
     4 int n,m,qq;
     5 int e[20010][3],p[4010],tot = 0,ans;
     6 int q[1010][2],dis[35][35],sq[4010],sdis[4010],head,tail;
     7 short vis[4010],map[35][35],dir[4][2] = {-1,0,1,0,0,-1,0,1}; //左右下上 
     8 int min(int a,int b)
     9 {
    10     return a<b?a:b;
    11 }
    12 void adde(int sn,int fn,int val)  //编号 将xy映射为编号 
    13 {
    14     e[++tot][0] = fn; e[tot][1] = val; e[tot][2] = p[sn]; p[sn] = tot;
    15 }
    16 void bfs(int si,int sj,int bi,int bj,int id)
    17 {
    18     int i,j,k,ii,jj;
    19     memset(dis,0,sizeof(dis));
    20     head = 1; tail = 2;
    21     q[1][0] = si; q[1][1] = sj; dis[si][sj] = 1;
    22     while(head != tail)
    23     {
    24         i = q[head][0]; j = q[head++][1];
    25         for(k=0;k<4;k++)
    26         {
    27             ii = i+dir[k][0]; jj = j+dir[k][1];
    28             if(!map[ii][jj] || (ii==bi&&jj==bj) || dis[ii][jj]) continue;
    29             dis[ii][jj] = dis[i][j]+1;
    30             q[tail][0] = ii; q[tail++][1] = jj;
    31         }
    32     }
    33     if(id == 4) return;
    34     for(k=0;k<4;k++)
    35     {
    36         i = bi+dir[k][0]; j = bj+dir[k][1];
    37         if((i == si && j == sj) || !dis[i][j]) continue;
    38         adde(bi*30*4+bj*4+id,bi*30*4+bj*4+k,dis[i][j]-1);
    39     }
    40     adde(bi*30*4+bj*4+id,si*30*4+sj*4+(id^1),1);
    41 }
    42 void spfa(int si,int sj)
    43 {
    44     int i,sn,fn,val;
    45     memset(sdis,60,sizeof(sdis));
    46     head = 1; tail = 1;
    47     for(i=0;i<4;i++)
    48     {
    49         if(!dis[si+dir[i][0]][sj+dir[i][1]]) continue;
    50         sn = si*30*4+sj*4+i;
    51         sq[tail++] = sn; sdis[sn] = dis[si+dir[i][0]][sj+dir[i][1]]-1; vis[sn] = 1;
    52     }
    53     while(head != tail)
    54     {
    55         sn = sq[head++];
    56         for(i=p[sn];i;i=e[i][2])
    57         {
    58             fn = e[i][0]; val = e[i][1];
    59             if(sdis[fn]<=sdis[sn]+val) continue;
    60             sdis[fn] = sdis[sn]+val;
    61             if(vis[fn]) continue;
    62             vis[fn] = 1; sq[tail++] = fn;
    63             if(tail>QLEN) tail = 1;
    64         }
    65         vis[sn] = 0;
    66         if(head>QLEN) head = 1;
    67     }
    68 }
    69 int main()
    70 {
    71     int i,j,bi,bj,si,sj,fi,fj;
    72     scanf("%d%d%d",&n,&m,&qq);
    73     for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%hd",&map[i][j]);
    74     for(i=1;i<=n;i++) for(j=1;j<=m;j++)
    75     {
    76         if(!map[i][j]) continue;
    77         if(map[i-1][j]) bfs(i-1,j,i,j,0);
    78         if(map[i+1][j]) bfs(i+1,j,i,j,1);
    79         if(map[i][j-1]) bfs(i,j-1,i,j,2);
    80         if(map[i][j+1]) bfs(i,j+1,i,j,3);
    81     }
    82     while(qq--)
    83     {
    84         scanf("%d%d%d%d%d%d",&bi,&bj,&si,&sj,&fi,&fj);
    85         if(si == fi && sj == fj) { printf("0
    "); continue;}
    86         bfs(bi,bj,si,sj,4); spfa(si,sj); ans = 999999;
    87         for(i=0;i<4;i++) ans = min(ans,sdis[fi*30*4+fj*4+i]);
    88         if(ans<999999) printf("%d
    ",ans);
    89         else printf("-1
    ");
    90     }
    91     return 0;
    92 }
  • 相关阅读:
    PAT 解题报告 1009. Product of Polynomials (25)
    PAT 解题报告 1007. Maximum Subsequence Sum (25)
    PAT 解题报告 1003. Emergency (25)
    PAT 解题报告 1004. Counting Leaves (30)
    【转】DataSource高级应用
    tomcat下jndi配置
    java中DriverManager跟DataSource获取getConnection有什么不同?
    理解JDBC和JNDI
    JDBC
    Dive into python 实例学python (2) —— 自省,apihelper
  • 原文地址:https://www.cnblogs.com/lidaxin/p/4859303.html
Copyright © 2011-2022 走看看