• 洛谷 P1979 华容道 解题报告


    P1979 华容道

    题目描述

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

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

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

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

    任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。

    游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

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

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

    输入输出格式

    输入格式:

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

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

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

    输出格式:

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

    说明

    对于 (30\%)的数据, (1 ≤ n, m ≤ 10,q = 1)

    对于 (60\%) 的数据, (1 ≤ n, m ≤ 30,q ≤ 10)

    对于 (100\%) 的数据, (1 ≤ n, m ≤ 30,q ≤ 500)


    对于我这种弱水平的选手,一定要好好练部分分。

    注意到我们可以在每次游戏枚举空白点位置和操作点位置进行搜索,则复杂度为(O(n^2m^2q)),在考场上想不到正解时,一定要把这部分分拿到。

    在洛谷神机下拿了80分,事实上应该只有60分
    60~80pts

    #include <cstdio>
    #include <queue>
    using namespace std;
    const int N=32;
    int g[N][N],n,m,vis[N][N][N][N],q;
    int ex,ey,sx,sy,tx,ty,t,ans;
    const int dx[5]={0,-1,0,1,0};
    const int dy[5]={0,0,1,0,-1};
    int abs(int x){return x>0?x:-x;}
    struct node
    {
        int x0,y0,x1,y1,step;
        node(int x0,int y0,int x1,int y1,int step)
        {
            this->x0=x0;this->y0=y0;this->x1=x1;this->y1=y1;this->step=step;
        }
    };
    void bfs()
    {
        queue <node > q;
        node st(ex,ey,sx,sy,0);
        q.push(st);
        while(!q.empty())
        {
            int x0=q.front().x0,y0=q.front().y0,x1=q.front().x1,y1=q.front().y1,step=q.front().step;
            if(x1==tx&&y1==ty) {ans=step;return;}
            q.pop();
            if(vis[x0][y0][x1][y1]==t) continue;
            vis[x0][y0][x1][y1]=t;
            if(x0==x1&&abs(y0-y1)==1)
            {
                node tt(x0,y1,x1,y0,step+1);
                q.push(tt);
            }
            if(y0==y1&&abs(x0-x1)==1)
            {
                node tt(x1,y0,x0,y1,step+1);
                q.push(tt);
            }
            for(int i=1;i<=4;i++)
            {
                int X=x0+dx[i],Y=y0+dy[i];
                if(g[X][Y]&&(X!=x1||Y!=y1))
                {
                    node tt(X,Y,x1,y1,step+1);
                    q.push(tt);
                }
            }
        }
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&g[i][j]);
        for(t=1;t<=q;t++)
        {
            scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
            ans=-1;bfs();
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    代码也不长

    后来胡乱搞了搞A*结果比暴力还慢,表示不会。。


    考虑正解,我们发现如果我们要移动的那个点要移动,则必须带着空白一起移动,否则没有意义。

    每次询问,我们可以先把空白移到初始点旁边再说,对之后的状态,一定是点带空白走

    对于原图的每个位置的点,它可以带四个方向的空白,于是把它拆成四个点建图。

    边为拆的四个点互相进行连边权值要求,和空白与点交换的边权1,可以在询问前预处理。

    然后对于每个询问,我们先移动空白点到初始点,然后枚举四个方向跑最短路即可

    注意不需要移动的情况,否则可能比暴力分还低,洛谷的数据是65分

    复杂度:(O(n^2m^m+q*(knm)))


    Code:

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=32;
    const int M=3610;
    const int inf=0x3f3f3f3f;
    int g[N][N],n,m,q,ex,ey,sx,sy,tx,ty;
    int cal(int x,int y,int towards)
    {
        return ((x-1)*m+y)+(towards-1)*n*m;
    }
    const int dx[5]={0,-1,0,1,0};
    const int dy[5]={0,0,1,0,-1};
    int head[M],to[M<<3],Next[M<<3],edge[M<<3],cnt;
    void add(int u,int v,int w)
    {
        to[++cnt]=v;Next[cnt]=head[u];edge[cnt]=w;head[u]=cnt;
    }
    struct node
    {
        int x,y,step;
        node(int x,int y,int step)
        {
            this->x=x;this->y=y;this->step=step;
        }
    };
    int used[N][N];
    int Dis(int x0,int y0,int Tx,int Ty)
    {
        memset(used,0,sizeof(used));
        queue <node > q;
        node t0(x0,y0,0);
        q.push(t0);
        while(!q.empty())
        {
            int x=q.front().x,y=q.front().y,step=q.front().step;
            q.pop();
            if(used[x][y]) continue;
            used[x][y]=1;
            if(x==Tx&&y==Ty) return step;
            for(int i=1;i<=4;i++)
            {
                int X=x+dx[i],Y=y+dy[i];
                if(g[X][Y])
                {
                    node tt(X,Y,step+1);
                    q.push(tt);
                }
            }
        }
        return inf;
    }
    void init()
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&g[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                if(!g[i][j]) continue;
                g[i][j]=0;
                for(int k=1;k<=4;k++)
                {
                    int X=dx[k]+i,Y=dy[k]+j;
                    if(!g[X][Y]) continue;
                    int u=cal(i,j,k),f=k>2?-1:1;
                    int v0=cal(X,Y,k+f*2);
                    add(u,v0,1),add(v0,u,1);
                    for(int l=k+1;l<=4;l++)
                    {
                        int tX=dx[l]+i,tY=dy[l]+j;
                        if(!g[tX][tY]) continue;
                        int v=cal(i,j,l),w=Dis(X,Y,tX,tY);
                        add(u,v,w),add(v,u,w);
                    }
                }
                g[i][j]=1;
            }
    }
    int dis[M],vis[M];
    void spfa(int s)
    {
        memset(dis,0x3f,sizeof(dis));
        queue <int > q;
        q.push(s);
        dis[s]=0;
        while(!q.empty())
        {
            int u=q.front();
            vis[u]=0;
            q.pop();
            for(int i=head[u];i;i=Next[i])
            {
                int v=to[i];
                if(dis[v]>dis[u]+edge[i])
                {
                    dis[v]=dis[u]+edge[i];
                    if(!vis[v]) vis[v]=1,q.push(v);
                }
            }
        }
    }
    void work()
    {
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
            if(tx==sx&&ty==sy) {printf("0
    ");continue;}
            int ans=inf;
            for(int j=1;j<=4;j++)
            {
                int X=sx+dx[j],Y=sy+dy[j];
                if(!g[X][Y]) continue;
                g[sx][sy]=0;
                int cnt0=Dis(ex,ey,X,Y);
                g[sx][sy]=1;
                int u=cal(sx,sy,j);
                spfa(u);
                for(int k=1;k<=4;k++)
                {
                    int tX=tx+dx[k],tY=ty+dy[k];
                    if(!g[tX][tY]) continue;
                    int v=cal(tx,ty,k);
                    ans=min(ans,dis[v]+cnt0);
                }
            }
            if(ans==inf) printf("-1
    ");
            else printf("%d
    ",ans);
        }
    }
    int main()
    {
        init();
        work();
        return 0;
    }
    

    2018.8.1

  • 相关阅读:
    Android开发之Toast的快去替换
    Android开发之Fragment的替换显示反复创建问题
    Android开发之多渠道打包
    Android开发之让其他软件调用自己播放器
    Android开发之的到屏幕的宽和高
    Android开发之视频播放调用setVideoPath()方法
    [luogu4931]情侣?给我烧了!
    CF449D Jzzhu and Numbers
    [PKUWC2018]猎人杀
    [BZOJ3028]食物
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9404326.html
Copyright © 2020-2023  润新知