zoukankan      html  css  js  c++  java
  • 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
    ",wrk());
        }
        return 0;
    }

    总结:

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

    但是就是没想到??

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

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

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

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

  • 相关阅读:
    深入学习高级非线性回归算法 --- 树回归系列算法
    监督学习中关于线性回归问题的系统讨论
    非均衡分类问题的思考与问题与解决思路
    使用 AdaBoost 元算法提高分类器性能
    支持向量机 (SVM)分类器原理分析与基本应用
    Logistic回归分类算法原理分析与代码实现
    mysql 数据库安装步骤个人总结
    mysql可重复读现象及原理分析
    ssm所需的jar详解
    获取客户端ip地址--getRemoteAddr()和getRemoteHost() 区别
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9554709.html
Copyright © 2011-2022 走看看