洛谷1979/codevs3290 华容道,74行就写完了?

题目分析

首先我们要明确,直接瞎广搜可以拿80分,所以怎么取舍是不是很明了?
如果你还是有一颗打正解的心…
好吧,祝你好运。
分析开始——
此题核心:状态为点,建图,进行最短路

状态为点

什么状态比较重要?目标棋子的位置,这是肯定的。再者是空格的位置。
不过事实上,重要的状态是空格和目标棋子相邻(第二步会说),也就是一个状态(x,y,tmp)是一个点,表示目标棋子在(x,y)位置,tmp代表空格的位置。我的代码中,tmp=0时表示在目标棋子上面,=1表示在下面,=2表示在左边,=3表示在右边。

建图

状态之间进行转移的代价为两点之间的边长。可以连边的节点有两种情况:
1.空格在不经过目标棋子的情况下移动到目标棋子身边另一位置。由于华容道的地图是不会改变的,所以这些边可以bfs暴力预先完成。
2.空格和目标棋子交换位置了,这两个状态之间的边长为1

进行最短路

首先bfs将空格移到目标棋子身边,然后就有四种可以作为起点的状态。因为我是使用spfa进行最短路的,所以只要合法(即空格可以移动到此位置),可以都加入处理队列。
然后spfa完毕后,有四种可能的终点(空格在目标棋子最终位置的不同方位),都查看一遍即可。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int n,m,q,tot;
int mp[33][33],dis[33][33],qx[1010],qy[1010];
int mvx[5]={-1,1,0,0},mvy[5]={0,0,-1,1};
int h[4010],to[40010],ne[40010],w[40010];
void add(int x,int y,int z)//建边
{to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void bfs(int sx,int sy,int bx,int by,int fx){//(sx,sy):空格位置,(bx,by):目标棋子位置
    int i,ta=1,he=1,xx,yy,tx,ty;
    memset(dis,0,sizeof(dis));
    qx[1]=sx,qy[1]=sy,dis[sx][sy]=1;
    while(he<=ta){
        xx=qx[he],yy=qy[he];
        for(i=0;i<=3;++i){
            tx=xx+mvx[i],ty=yy+mvy[i];
            if(mp[tx][ty]&&!dis[tx][ty]&&(tx!=bx||ty!=by))//注意这些限制条件
                dis[tx][ty]=dis[xx][yy]+1,qx[++ta]=tx,qy[ta]=ty;
        }
        ++he;
    }
    if(fx==4)return;
    for(i=0;i<=3;++i){//情况1:空格在不经过目标棋子的情况下来到目标棋子身边其他方位
        tx=bx+mvx[i],ty=by+mvy[i];
        if((tx!=sx||ty!=sy)&&dis[tx][ty])
            add(bx*120+by*4+fx,bx*120+by*4+i,dis[tx][ty]-1);
    }
    add(bx*120+by*4+fx,sx*120+sy*4+fx^1,1);//情况2:交换空格与目标棋子的位置
}
int d[4010],inq[4010];
void spfa(int bx,int by){
    int i,x,tx,ty,tt;queue<int>q;
    for(i=0;i<=4000;++i)d[i]=1e7,inq[i]=0;
    for(i=0;i<=3;++i){//有4种可行的开始情况
        tx=bx+mvx[i],ty=by+mvy[i],tt=bx*120+by*4+i;
        if(dis[tx][ty])d[tt]=dis[tx][ty]-1,q.push(tt),inq[tt]=1;
    }
    while(!q.empty()){
        x=q.front(),q.pop(),inq[x]=0;
        for(i=h[x];i!=-1;i=ne[i])
            if(d[x]+w[i]<d[to[i]]){
            d[to[i]]=d[x]+w[i];
            if(!inq[to[i]])inq[to[i]]=1,q.push(to[i]);
        }
    }
}
int main()
{
    int i,j,sx,sy,mx,my,bx,by,ans;
    scanf("%d%d%d",&n,&m,&q);memset(h,-1,sizeof(h));
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)scanf("%d",&mp[i][j]);
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j){
        if(!mp[i][j])continue;
        if(mp[i-1][j])bfs(i-1,j,i,j,0);
        if(mp[i+1][j])bfs(i+1,j,i,j,1);
        if(mp[i][j-1])bfs(i,j-1,i,j,2);
        if(mp[i][j+1])bfs(i,j+1,i,j,3);
    }
    while(q--){
        scanf("%d%d%d%d%d%d",&sx,&sy,&bx,&by,&mx,&my);
        if(bx==mx&&by==my){puts("0");continue;}
        bfs(sx,sy,bx,by,4);spfa(bx,by);ans=1e7;
        for(i=0;i<=3;++i)ans=min(ans,d[mx*120+my*4+i]);//有4种可行的结束情况
        if(ans<1e7)printf("%d\n",ans);
        else puts("-1");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值