noip2013day2压轴题 华容道

题目链接
首先这是一道毒瘤题,写了一下午没有瞎弄出来,看了题解才有所领悟
以上都是一些废话


分析

开始想过70分的暴力,直接暴力BFS,答案离线,起点相同的一起做;试过不止70分
首先很容易看出来这是一道图论题,显然是要用到最短路,确定d数组状态d[i][j][k]表示起点到(i,j),k方向是白块的最短路径,但是由于图的特性,建图,好像有点鬼畜,死磕2小时无所获,于是果断看题解
有一个特点就是事实上移动棋子就是移动白块;
格子S想要走到与它相邻的k方向(left,right,up,down)上的格子,必须先把E格子通过w的花费移到某个格子,然后在用1的花费使S走到那 里,总花费为w+1,由于从一个格子向相邻方向上走一步的花费要多次使用,因此我们把它预处理成数组a[x][y][k][h],表示从(x,y)这个棋子,白格子在k方向上,使棋子走到h方向上相邻的格子的最少花费,这个可以用BFS预处理
然后就是最短路的过程,模板化,数据用spfa就能过,好吧我懒得写Dijkstra
答案在先处理就好了。代码附注释。

#include<bits/stdc++.h>
#define up 1
#define down 2
#define left 3
#define right 4//方向
#define N 35
#define inf 0x3f3f3f3f
using namespace std;
struct node{int x,y,k;};//点状态
int p[N][N],d[N][N][5],Ex,Ey,a[N][N][5][5],deep[N][N],n,m;
bool in[N][N][5],done[N][N];
queue<node>q,qb;
int other(int k){
    if(k==up)return down;
    if(k==down)return up;
    if(k==left)return right;
    if(k==right)return left;
} //相反方向移动
node go(node n,int k){
    node t=n;
    if(k==up)t.x--;
    if(k==down)t.x++;
    if(k==left)t.y--;
    if(k==right)t.y++;
    return t;
} //移动棋子
int bfs(node s,node t){
    if(p[s.x][s.y]==0||p[s.x][s.y]==0)return inf;//如果两点中有固定点,则不能到达
    memset(deep,0x3f,sizeof(deep));
    memset(done,0,sizeof(done));
    qb=*(new queue<node>);//清空队列很重要<s>被坑</s>
    qb.push(s);
    deep[s.x][s.y]=0;done[s.x][s.y]=true;
    while(!qb.empty()&&!done[t.x][t.y]){
        node u=qb.front();
        qb.pop();
        for(int k=1;k<=4;k++){
            node v=go(u,k);
            if(!done[v.x][v.y]&&p[v.x][v.y]==1){
                done[v.x][v.y]=true;
                qb.push(v);
                deep[v.x][v.y]=deep[u.x][u.y]+1;
            }
        }
    }
    return deep[t.x][t.y];
}//基本宽搜操作
void init(){
    memset(a,0x3f,sizeof(a));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(p[i][j]==0)continue;
            p[i][j]=0;//把点封死,防止冗余的BFS 
            for(int k=1;k<=4;k++){
                for(int h=1;h<=4;h++){
                    if(h<k){
                        a[i][j][k][h]=a[i][j][h][k];
                        continue;
                    }//根据对称性,加快效率
                    node t1=go((node){i,j},k);
                    node t2=go((node){i,j},h);
                    if(p[t1.x][t1.y]==0||p[t2.x][t2.y]==0)continue;//判断固定点
                    a[i][j][k][h]=bfs(t1,t2)+1;//w+1的操作
                }
            }
            p[i][j]=1;//恢复点的连通
        }
}
int spfa(node S,node T){
    if(S.x==T.x&&S.y==T.y)return 0;
    memset(d,0x3f,sizeof(d));
    memset(in,0,sizeof(in));
    q=*(new queue<node>);
    if(p[S.x][S.y]==0||p[T.x][T.y]==0)return inf;
    p[S.x][S.y]=0;
    for(int k=1;k<=4;k++){
        q.push((node){S.x,S.y,k});
        in[S.x][S.y][k]=true;
        d[S.x][S.y][k]=bfs((node){Ex,Ey},go(S,k));//计算白格子到起点的步数
    }//队列初始化
    p[S.x][S.y]=1;
    while(!q.empty()){
        node u=q.front();
        q.pop();
        in[u.x][u.y][u.k]=false;
        for(int h=1;h<=4;h++){
            node v=go(u,h);
            v.k=other(h);//移动后白格子所在的位置相反
            if(d[u.x][u.y][u.k]+a[u.x][u.y][u.k][h]<d[v.x][v.y][v.k]){
                d[v.x][v.y][v.k]=d[u.x][u.y][u.k]+a[u.x][u.y][u.k][h];
                if(!in[v.x][v.y][v.k]){
                    q.push(v);
                    in[v.x][v.y][v.k]=true;
                }
            }//执行松弛操作
        }
    }
    int res=inf;
    for(int k=1;k<=4;k++)
        res=min(res,d[T.x][T.y][k]);//得到答案
    return res;
}
int main(){
    int Q,sx,sy,tx,ty;
    scanf("%d%d%d",&n,&m,&Q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&p[i][j]);
    init();//预处理
    for(int i=1;i<=Q;i++){
        scanf("%d%d%d%d%d%d",&Ex,&Ey,&sx,&sy,&tx,&ty);
        int ans=spfa((node){sx,sy},(node){tx,ty});
        if(ans<inf)printf("%d\n",ans);
        else printf("-1\n");
    }//在线操作
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36316033/article/details/82717852