NOIP 2013 华容道

题目描述

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

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

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

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

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

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

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

 

【数据范围】

对于30% 30\%30%的数据,1≤n,m≤10,q=11 ≤ n, m ≤ 10,q = 11n,m10,q=1;

对于 60%60\%60%的数据,1≤n,m≤30,q≤101 ≤ n, m ≤ 30,q ≤ 101n,m30,q10;

对于 100%100\%100%的数据,1≤n,m≤30,q≤5001 ≤ n, m ≤ 30,q ≤ 5001n,m30,q500。

 

题解:

这个题,竟然闲的无事打了一个上午的暴力?!?!?!

可以很容易地想到,指定的棋子想要移动,必须把空格移到它的旁边。

所以,对于每组数据,我们必须先要bfs找到空格到棋子的四个方向的距离。

以这四个初始状态开始,找到到终点的最短距离。

基本上是循环这个操作:把空格子移到棋子的某个位置,(不能经过棋子自己),把棋子挪到空格上。

发现,每次空格移到棋子某个位置可以bfs,O(nm)

棋子的所有移动位置也是O(nm)

用一个spfa或者dij,就可以处理最短路了。

但是,复杂度就很高了,O(qn^2m^2log(nm))

发现,缺点在于,我们每次空格移到棋子的另一个位置,都要bfs一遍,实际上同一种情况会算重很多次。

所以,我们预处理啊!(多组询问经典套路处理方法)

 

设tt[x][y][d1][d2]表示,从(x,y)的d1方向的空格,不经过(x,y)到棋子的d2方向的空格,所花费的最短路。

这个枚举可以预处理。O(8n^2m^2)

然后,在每一个询问里面,空格互相移动的时候,直接查表好啦!!

复杂度:O(qnmlog(nm))(dijkstra最短路)

 

代码:

 这里没有像一些代码建边,因为,其实边我们都是知道的。

而且,边的建造有一定规律,可以直接手动枚举出来,不需要再花费时空建边了。

尤其是在差分约束的时候,相邻的边直接枚举就好了,省了很多功夫。

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=32;
int n,m;
int mp[N][N];
int tt[N][N][4][4];
int mv[4][2]={{+1,0},{0,+1},{0,-1},{-1,0}};
int dis[N][N][4];
bool vis[N][N];
bool go[N][N][4];
int ex,ey,sx,sy,tx,ty;

struct duilie{
    int x,y,has;
}que[N*N];

int l,r;
int bfs1(int gx,int gy){//from ex,ey to gx,gy not pass sx,sy
    
    memset(vis,0,sizeof vis);
    l=1,r=0;
    que[++r].x=ex,que[r].y=ey;
    que[r].has=0;
    vis[ex][ey]=1;
    
    vis[sx][sy]=1;
    
    while(l<=r){
        duilie now=que[l++];
        if(now.x==gx&&now.y==gy) return now.has;
        for(int i=0;i<4;i++){
            int dx=now.x+mv[i][0];
            int dy=now.y+mv[i][1];
            if(dx<1||dx>n) continue;
            if(dy<1||dy>m) continue;
            if(vis[dx][dy]) continue;
            if(!mp[dx][dy]) continue;
            
            vis[dx][dy]=1;
            que[++r].x=dx,que[r].y=dy;
            que[r].has=now.has+1;
        }
    }
    return -1;
}

int bfs2(int x,int y,int gx,int gy,int nx,int ny){// from (x,y) to (gx,gy) not pass (nx,ny);
    
    memset(vis,0,sizeof vis);
    
    vis[x][y]=1;
    vis[nx][ny]=1;
    
    l=1;r=0;
    
    que[++r].x=x,que[r].y=y;
    que[r].has=0;
    
    while(l<=r){
        duilie now=que[l++];
        if(now.x==gx&&now.y==gy) return now.has;
        
        for(int i=0;i<4;i++){
            int dx=now.x+mv[i][0];
            int dy=now.y+mv[i][1];
            
            if(dx<1||dx>n) continue;
            if(dy<1||dy>m) continue;
            if(vis[dx][dy]) continue;
            if(!mp[dx][dy]) continue;
            
            vis[dx][dy]=1;
            
            que[++r].x=dx,que[r].y=dy;
            que[r].has=now.has+1;
        }
    }
    return -1;
        
}

struct node{
    int x,y,d;
    int sum;
    bool friend operator <(node a,node b){
        return a.sum>b.sum;
    }
};
priority_queue<node>q;

int wrk(){
    
    if(!mp[sx][sy]) return -1;
    if(!mp[tx][ty]) return -1;
    if(!mp[ex][ey]) return -1;
    if(sx==tx&&sy==ty) return 0;
    
    memset(dis,inf,sizeof dis);
    
    for(int i=0;i<4;i++){
        
        int dx=sx+mv[i][0],dy=sy+mv[i][1];
        
        if(!mp[dx][dy]) continue;
        if(dx<1||dx>n) continue;
        if(dy<1||dy>m) continue;
        
        int dist=bfs1(dx,dy);
        
        if(dist!=-1){
            dis[sx][sy][i]=dist;
            node lp;
            lp.x=sx,lp.y=sy,lp.d=i;
            lp.sum=dist;
            q.push(lp);
        }
    }
    
    memset(go,0,sizeof go);
    
    while(!q.empty()){
        node now=q.top();q.pop();
        if(go[now.x][now.y][now.d]) continue;
        go[now.x][now.y][now.d]=1;
        dis[now.x][now.y][now.d]=now.sum;
        
        for(int i=0;i<4;i++){
            if(i==now.d) continue;
            int dx=now.x+mv[i][0],dy=now.y+mv[i][1];
            
            if(!mp[dx][dy]) continue;
            if(dx<1||dx>n) continue;
            if(dy<1||dy>m) continue;
            
            int dist=tt[now.x][now.y][now.d][i];
            
            if(dist!=-1){
                if(!go[now.x][now.y][i]){
                    node lp;
                    lp.x=now.x,lp.y=now.y;
                    lp.d=i;
                    lp.sum=now.sum+dist;
                    q.push(lp);
                }
            }
        }
        
        int dx=now.x+mv[now.d][0],dy=now.y+mv[now.d][1];
        if(!go[dx][dy][3-now.d]){
            node lp;
            lp.x=dx,lp.y=dy;
            lp.d=3-now.d;
            lp.sum=now.sum+1;
            q.push(lp);
        }
    }
    
    int ret=inf;
    for(int i=0;i<4;i++){
        ret=min(ret,dis[tx][ty][i]);
    }
    if(ret==inf) return -1;
    else return ret;
}
void clear(){
    while(!q.empty()) q.pop();
    
}
int T;
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&mp[i][j]);
        }
    }
    for(int i=1;i<=n;i++){//pre work
        for(int j=1;j<=m;j++){
            if(!mp[i][j]) continue;
            
            for(int k=0;k<4;k++){
                int kx=i+mv[k][0],ky=j+mv[k][1];
                if(!mp[kx][ky]) continue;
                if(kx<1||kx>n) continue;
                if(ky<1||ky>m) continue;
                
                for(int p=k+1;p<4;p++){
                    int px=i+mv[p][0],py=j+mv[p][1];    
                    
                    if(!mp[i][j]) continue;
                    if(px<1||px>n) continue;
                    if(py<1||py>m) continue;
                    
                    int dist=bfs2(kx,ky,px,py,i,j);
                    tt[i][j][k][p]=tt[i][j][p][k]=dist;
                    
                }
            }
        }
    }
    
    
    while(T--){
        scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
        clear();
        printf("%d\n",wrk());
    }
    return 0;
}

 

总结:

其实思路挺简单的。暴力处理比较好想,然后优化也比较自然就出来了。

但是就是没想到??

还有,不能直接bfs最短路,因为边权不一定是1啊!!

以后对多组询问预处理还是要注意,

经常会有比较耗时间的地方,却还要算多次。

(相比较于搜索,dp时间的优化,本质上就是重复状态的一次计算——ZRT)

 

 

 

 

 

 

posted @ 2018-08-29 15:38  *Miracle*  阅读(266)  评论(0编辑  收藏  举报