洛谷 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\n");//!!!
        else printf("%d\n",work(ex,ey,sx,sy,tx,ty));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/9700718.html