zoukankan      html  css  js  c++  java
  • 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路

    题目:https://www.luogu.org/problemnew/show/P1979

    真是一道好题...

    首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 bfs 转移;

    然后发现,这些状态中有很多无用的,换句话说,就是仅当空格在指定棋子旁边时才有用,能带来棋子的移动;

    所以不妨只记录 f[i][j][k] ,k 表示空格在指定棋子 (i,j) 的哪个方向;

    两种转移:1.指定棋子不动,空格从它的一边移动到另一边;

    2.指定棋子和空格交换位置;

    所以给可以相互转移的状态之间连边,SPFA 跑最短路;

    初始状态空格不一定在指定棋子旁边,所以专门 bfs 一下,得到空格在指定棋子旁边的状态;

    由于空格可以在指定棋子的四个方向,所以要跑多源最短路;

    连边时给每个状态直接记一个 id 比较方便;

    调了半天终于65分,加个特判:起点就是终点时输出0,就 A 了~

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int const xn=35,xm=4005,inf=1e9;
    int n,m,Q,f[xn][xn][5][5],id[xn][xn][5],dx[5]={-1,1,0,0},dy[5]={0,0,-1,1};//0,1,2,3 -> 上下左右
    int hd[xm],ct,to[xm<<1],nxt[xm<<1],dis[xm<<1],w[xm<<1];
    bool v[xn][xn],vis[xn][xn],vis2[xm];
    struct N{int x,y,d;};
    queue<N>q;
    queue<int>q2;
    void add(int x,int y,int z){to[++ct]=y; nxt[ct]=hd[x]; w[ct]=z; hd[x]=ct;}
    int rd()
    {
        int ret=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
        while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return f?ret:-ret;
    }
    bool ck(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m&&!v[x][y];}
    int bfs(int x,int y,int p1,int p2)
    {
        while(q.size())q.pop();
        memset(vis,0,sizeof vis);
        int sx=x+dx[p1],sy=y+dy[p1];
        int xx=x+dx[p2],yy=y+dy[p2];
        if(!ck(sx,sy)||!ck(xx,yy))return inf;
        q.push((N){sx,sy,0});
        vis[sx][sy]=1; vis[x][y]=1;//!!
        while(q.size())
        {
            int tx=q.front().x,ty=q.front().y,d=q.front().d; q.pop();
            for(int i=0,nx,ny;i<4;i++)
            {
                nx=tx+dx[i]; ny=ty+dy[i];
                if(!ck(nx,ny)||vis[nx][ny])continue;
                if(nx==xx&&ny==yy)return d+1;
                else q.push((N){nx,ny,d+1}),vis[nx][ny]=1;
            }
        }
        return inf;
    }
    void init()
    {
        int cnt=0; 
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int k=0;k<4;k++)
                {
                    id[i][j][k]=++cnt;
                    if(v[i][j])continue;
                    for(int l=k+1;l<4;l++)
                        f[i][j][k][l]=bfs(i,j,k,l);
                }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int k=0;k<4;k++)
                {
                    int x=i+dx[k],y=j+dy[k],a=id[i][j][k],b;
                    if(!ck(x,y))continue;
    //                for(int l=0;l<4;l++)   //i,j表示指定棋子位置,不可有共用空格的点之间连0的边!(否则没算上指定棋子的移动代价)
    //                {
    //                    int nx=x+dx[l],ny=y+dy[l];
    //                    if(!ck(nx,ny)||(nx==i&&ny==j))continue;
    //                    b=id[nx][ny][l^1];
    //                    add(a,b,0); add(b,a,0);
    //                }
                    if(v[i][j])continue;
                    for(int l=k+1;l<4;l++)
                    {
                        int nx=i+dx[l],ny=j+dy[l];
                        if(!ck(nx,ny)||f[i][j][k][l]==inf)continue;
                        b=id[i][j][l];
                        add(a,b,f[i][j][k][l]); add(b,a,f[i][j][k][l]);
                    }
                    b=id[x][y][k^1];
                    add(a,b,1); add(b,a,1);
                }
    }
    int is(int x,int y,int xx,int yy)
    {
        for(int k=0;k<4;k++)
            if(x+dx[k]==xx&&y+dy[k]==yy)return k;
        return -1;
    }
    int work(int ex,int ey,int sx,int sy,int tx,int ty)
    {
        while(q.size())q.pop(); while(q2.size())q2.pop();
        memset(vis,0,sizeof vis);
        memset(vis2,0,sizeof vis2);
        memset(dis,0x3f,sizeof dis);
        q.push((N){ex,ey,0}); vis[ex][ey]=1;
        vis[sx][sy]=1;//!!
        int k=is(sx,sy,ex,ey),bh;
        if(k!=-1)q2.push(bh=id[sx][sy][k]),vis2[bh]=1,dis[bh]=0;
        while(q.size())
        {
            int x=q.front().x,y=q.front().y,d=q.front().d; q.pop();
            for(int i=0;i<4;i++)
            {
                int tx=x+dx[i],ty=y+dy[i];
                if(!ck(tx,ty)||vis[tx][ty])continue;
                int k=is(sx,sy,tx,ty); int bh;
                if(k!=-1)q2.push(bh=id[sx][sy][k]),vis2[bh]=1,dis[bh]=d+1;
                q.push((N){tx,ty,d+1});
                vis[tx][ty]=1;
            }
        }
        while(q2.size())
        {
            int x=q2.front(); q2.pop(); vis2[x]=0;
            for(int i=hd[x],u;i;i=nxt[i])
            {
                u=to[i];
                if(dis[u=to[i]]>dis[x]+w[i])
                {
                    dis[u]=dis[x]+w[i];
                    if(!vis2[u])q2.push(u),vis2[u]=1;
                }
            }
        }
        int ans=inf;
        for(int k=0;k<4;k++)ans=min(ans,dis[id[tx][ty][k]]);
        if(ans==inf)return -1;
        return ans;
    }
    int main()
    {
        n=rd(); m=rd(); Q=rd();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)v[i][j]=!rd();
        init();
        for(int i=1,ex,ey,sx,sy,tx,ty;i<=Q;i++)
        {
            ex=rd(); ey=rd(); sx=rd(); sy=rd(); tx=rd(); ty=rd();
            if(sx==tx&&sy==ty)printf("0
    ");//!!!
            else printf("%d
    ",work(ex,ey,sx,sy,tx,ty));
        }
        return 0;
    }
  • 相关阅读:
    Hadoop功能模块之hdfs
    Hadoop介绍
    大数据的介绍
    Hadoop之shell命令
    Flume
    C# DataTable使用方法详解
    npoi 操作excell 可以下载的链接
    node.js mqtt样例
    node.js压缩
    arcgis中打印所有变量的名称和值
  • 原文地址:https://www.cnblogs.com/Zinn/p/9700718.html
Copyright © 2011-2022 走看看