zoukankan      html  css  js  c++  java
  • noip2013Day2T3-华容道【一个蒟蒻的详细题解】

    描述

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

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

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

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

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

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

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

    格式

    输入格式

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

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

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

    输出格式

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

    样例1

    样例输入1

     
    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
    

    样例输出1

     
    2 
    -1
    

    限制

    每个测试点1s。

    提示

    样例说明

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

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

      移动过程如下:

      img

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

      img

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

    数据范围

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

    题解

    就是啊,公子还是把这道题过了……【虽然抄了题解的思路但是公子没有抄代码啊

    嗯,用蒟蒻的语言给蒟蒻碰上的人用蒟蒻的方式讲讲吧……

    windows画图是神器,嗯。

    【1】首先我们需要一个曹操华容道,然后我们观察棋子的性质。

    (1)我们可以把移动棋子当做移动白格【显然】

    (2)我们只需要关注的是起始棋子【显然】

    (3)结合(1)(2)我们需要拿白格子去给起始棋子铺路【显然】

    (4)那么如果起始棋子旁边没有白格子时,我们需要把白格子搞到它旁边。【显然】

    (5)那么(4)之后我们如果要把起始棋子(在不与上一次移动相反的情况下)向某一方向移动时,我们要把白格子铺在当前棋子位置这一方向【……显然?】

    (6)(5)中的操作白格子永远在当前起始棋子位置的四周。【显然?】

    【暴搜超时的要点其实是(6),当瞎搞的时候算了很多很多很多遍的让白格子从(x,y)的从上到右,从上到下,从下到左……这样的白格子铺路移动】

    (7)那么所有位置可能是放棋子的位置。【显然】

    (8)预处理所有位置上下左右四个方向到另外的上下左右四个方向。【………………】

    【1】的(4)实现方法:

    空白格子对起始格子的四周进行暴搜路径大小。

    (9)(8)的方法同上,但暴搜不能经过(x,y)原位置,那就相当于重复移动。

    这两个都是非常简单的宽搜。

    嗯。我都讲到这个份上了,预处理就可以结束了。

    (因为这个图不优美,并不一定是曼哈顿距离。)

    【2】我们还需要一个关羽最短路。

    (1)为什么需要最短路呢,因为题里说要求最短路【显然】

    (2)这张图很工整,可以跑spfa【显然】

    (3)上面两句都是废话【……】

    (4)我们初始塞进去初始棋子上下左右的四个状态【显然

    (5)我们移动的时候还是需要白格子铺路的代价【显然】

    (6)然而我们已经在【1】预处理出来了!【鼓掌】

    (7)但是我们发现对于(x,y),从(x-1,y)和(y+1,x)移动来的【诸如此类的】并不一定是一个状态(白格子的方位不同)。【哦QAQ】

    (8)但是由于图只有30*30那么大,所以我们多开一维记录方向。【嘿嘿嘿】

    (9)然后就变成了一个裸的spfa,带了方向转移即可。【嘿嘿嘿】

    【spfa那么简单我拒绝画图。】

    题做完了。

    代码量↑↑↑……自己写写看吧。

    不要抄我的,我写的丑,出门可以转到很多代码优美的神犇那里。

    还有我题解写得那么详尽就不要抄了orz。

    【不要看↓,这是一个蒟蒻存代码的地方,不要伤害我了QAQ】

      1 #include <iostream>
      2 #include <string.h>
      3 #include <cstdlib>
      4 #include <cstdio>
      5 #include <algorithm>
      6 #include <cstring>
      7 #include <vector>
      8 #include <ctime>
      9 #include <queue>
     10 //#define ivorysi
     11 #define mo 10007
     12 #define siji(i,x,y) for(int i=(x);i<=(y);i++)
     13 #define gongzi(j,x,y) for(int j=(x);j>=(y);j--)
     14 #define xiaosiji(i,x,y) for(int i=(x);i<(y);i++)
     15 #define sigongzi(j,x,y) for(int j=(x);j>(y);j--)
     16 #define ivory(i,x) for(int i=head[x];i;i=edge[i].next)
     17 #define pii pair<int,int>
     18 #define fi first
     19 #define se second
     20 #define inf 0x5f5f5f5f
     21 typedef long long ll;
     22 using namespace std;
     23 struct data{
     24     int x,y,k,step;
     25 };
     26 struct node {
     27     int x,y,k;
     28 };
     29 int dirx[5]={0,-1,1,0,0},diry[5]={0,0,0,-1,1};
     30 int op[5]={0,2,1,4,3};
     31 int n,m,q;
     32 queue<data> q1;
     33 queue<node> q2;
     34 int graph[35][35],wmove[35][35][5][5];
     35 int dist[35][35][4];
     36 bool vis[35][35][4];
     37 bool vis1[35][35];
     38 int ex,ey,sx,sy,tx,ty;
     39 void pre(int x,int y) {//暴搜处理四周格子部分
     40     siji(i,1,4) {
     41         if(!graph[x+dirx[i]][y+diry[i]]) continue;
     42         siji(j,1,4) {
     43             if(i==j || wmove[x][y][i][j]) continue;
     44             int xx=x+dirx[j],yy=y+diry[j];
     45             if(!graph[xx][yy]) continue;
     46             siji(k,1,n) siji(l,1,m){
     47                 vis1[k][l]=0;
     48             }
     49             while(!q1.empty()) q1.pop();
     50             q1.push((data){x+dirx[i],y+diry[i],i,0});
     51             vis1[x+dirx[i]][y+diry[i]]=1;
     52             int ans=-1;
     53             while(!q1.empty()) {
     54                 data tmp=q1.front();q1.pop();
     55                 if(tmp.x==xx && tmp.y==yy) {ans=tmp.step;break;}
     56                 siji(l,1,4) {
     57                     if(l!=op[tmp.k]) {
     58                         int nx=tmp.x+dirx[l],ny=tmp.y+diry[l];
     59                         if((nx!=x || ny!=y ) && graph[nx][ny] &&(!vis1[nx][ny])) {
     60                             q1.push((data){nx,ny,l,tmp.step+1});
     61                             vis1[nx][ny]=1;
     62                         }
     63                     }
     64                 }
     65             }
     66             wmove[x][y][i][j]=wmove[x][y][j][i]=ans;
     67         }
     68         
     69     }
     70 }
     71 void pre_plain() {//处理ex,ey到sx,sy四周的部分
     72     siji(i,1,4) {
     73         int xx=sx+dirx[i],yy=sy+diry[i];
     74         if(!graph[xx][yy]) continue;
     75         while(!q1.empty()) q1.pop();
     76         int ans=inf;
     77         q1.push((data){ex,ey,0,0});
     78 
     79         siji(k,1,n) siji(l,1,m){
     80             vis1[k][l]=0;
     81         }
     82         vis1[ex][ey]=1;
     83         while(!q1.empty()) {
     84 
     85             data tmp=q1.front();q1.pop();
     86             if(tmp.x==xx && tmp.y==yy) {ans=tmp.step;break;}
     87             siji(l,1,4) {
     88                 if(l!=op[tmp.k]) {
     89                     int nx=tmp.x+dirx[l],ny=tmp.y+diry[l];
     90                     if((nx!=sx || ny!=sy) && graph[nx][ny] && (!vis1[nx][ny])) {
     91                         q1.push((data){nx,ny,l,tmp.step+1});
     92                         vis1[nx][ny]=1;
     93                     }
     94                 }
     95             }
     96         }
     97         if(ans<inf) {//顺手把状态塞进去
     98             dist[xx][yy][i]=ans+1;
     99             vis[xx][yy][i]=1;
    100             q2.push((node){xx,yy,i});
    101         }
    102     }
    103 }
    104 void solve() {
    105     while(!q2.empty()) q2.pop();
    106     siji(i,1,n) siji(j,1,m) siji(l,1,4){
    107         dist[i][j][l]=inf;
    108         vis[i][j][l]=0;
    109     }
    110     pre_plain();
    111     int ans=inf;
    112     if(tx==sx && ty==sy) ans=0;//不知道这个特判有没有用
    113     while(!q2.empty()) {
    114         node tmp=q2.front();q2.pop();vis[tmp.x][tmp.y][tmp.k]=0;
    115         if(tmp.x==tx && tmp.y==ty) {//搜到了就停
    116             ans=min(ans,dist[tmp.x][tmp.y][tmp.k]);
    117             continue;
    118         }
    119         siji(i,1,4) {
    120             if(i!=op[tmp.k]) {
    121                 int nx=tmp.x+dirx[i],ny=tmp.y+diry[i];
    122                 if(!graph[nx][ny]) continue;
    123                 int val=wmove[tmp.x][tmp.y][op[tmp.k]][i];
    124                 if(val<=0) continue;//此时说明这个方向格子走不动
    125                 if(dist[nx][ny][i]>dist[tmp.x][tmp.y][tmp.k]+val+1){
    126                     dist[nx][ny][i]=dist[tmp.x][tmp.y][tmp.k]+val+1;
    127                     if(!vis[nx][ny][i]) {
    128                         vis[nx][ny][i]=1;
    129                         q2.push((node){nx,ny,i});
    130                     }
    131                 }
    132             }
    133         }
    134     }
    135     if(ans>=inf || ans<0) ans=-1;
    136     printf("%d
    ",ans); 
    137 }
    138 void Main() {
    139     scanf("%d%d%d",&n,&m,&q);
    140     siji(i,1,n) {
    141         siji(j,1,m) {
    142             scanf("%d",&graph[i][j]);
    143         }
    144     }
    145     siji(i,1,n) {
    146         siji(j,1,m) {
    147             if(graph[i][j]) pre(i,j);
    148         }
    149     }
    150     siji(i,1,q) {
    151         scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
    152         solve();
    153     }
    154 }
    155 int main() {
    156     Main();
    157 }
    华容道
  • 相关阅读:
    java实现第五届蓝桥杯神奇算式
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯扑克序列
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
    java实现第五届蓝桥杯圆周率
  • 原文地址:https://www.cnblogs.com/ivorysi/p/5879100.html
Copyright © 2011-2022 走看看