【神题】NOIP2013华容道

【神题】NOIP2013华容道_第1张图片

借大神的一句话:在考场上遇到这种题就只有呵呵了。

当时以为是一道搜索题,然后用了BFS,双向BFS,A*,发现还是BFS最快- -。(可能我的A*写丑了…)然而只骗了45分。据说有大神的BFS搜索得了80分……

正解:预处理+最短路。
先预处理出可能的目标格子(即所以可移动的格子)到达四周格子(假定是空格)的最短路。
处理每一次询问时,预处理出空格到目标格子四周格子(当然不能经过目标格子)的最少步数。之后将状态(i,j,k)(表示目标格子到达点(i,j)并且空格在该点的k方向)当做一个点,用 tmp[i][j][k] 表示到达该状态需要的最少步数。做一次最短路。

扩展状态:
1.空格与格子交换。
2.空格换到格子的其他方向。

果真是一道神题,在此膜拜在赛场上AC的大牛们。%%%

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define MAXN 35
using namespace std;
const int inf=0x3f3f3f3f;

int dis[MAXN][MAXN] ,step[MAXN][MAXN][4][4] ,tmp[MAXN][MAXN][4] ;
bool in[MAXN][MAXN][4] ;
int map[MAXN][MAXN] ,n ,m ,x0 ,y0 ,xb ,yb ,xe ,ye ;
int d1[4]={0,1,0,-1};
int d2[4]={1,0,-1,0};

struct node
{
    int x ,y ,k ;
};
queue<node>myque ;

struct aaa
{
    int x ,y ;
};
queue<aaa>q;

void init(int a,int b)//预处理目标棋子在位置(a,b)时,到达相邻格子所需的步数(当然不能经过(a,b))
{
    int dx ,dy ;
    for(int i=0;i<4;++i)
    {
        dx=a+d1[i] ,dy=b+d2[i] ;
        if(dx<1||dy<1||dx>n||dy>m)continue;
        if(map[dx][dy])
            myque.push((node){dx,dy,i});
    }
    int x ,y ,k ,temp ,temp2 ;
    while(!myque.empty())
    {
        memset(dis,inf,sizeof dis);
        x=myque.front().x ,y=myque.front().y ,k=myque.front().k ;
        myque.pop();
        dis[x][y]=0 ;
        q.push((aaa){x,y});
        while(!q.empty())
        {
            temp=q.front().x ,temp2=q.front().y ;
            q.pop();
            for(int i=0;i<4;++i)
            {
                dx=temp+d1[i] ,dy=temp2+d2[i] ;
                if(dx<1||dy<1||dx>n||dy>m)continue;
                if(map[dx][dy]&&(dx!=a||dy!=b)&&dis[dx][dy]==inf)
                {
                    dis[dx][dy]=dis[temp][temp2]+1;
                    q.push((aaa){dx,dy});
                }
            }
        }
        for(int i=0;i<4;++i)
        {
            dx=a+d1[i] ,dy=b+d2[i] ;
            if(dx<1||dy<1||dx>n||dy>m)continue;
            if(map[dx][dy]&&(dx!=x||dy!=y)&&dis[dx][dy]!=inf)
                step[a][b][k][i]=dis[dx][dy];
        }
    }
}

void pre()//预处理当前空格(x0,y0)到目标格子相邻格子的最短路
{
    memset(dis,inf,sizeof dis);
    dis[x0][y0]=0;
    q.push((aaa){x0,y0});

    int x ,y ,dx ,dy ;

    while(!q.empty())
    {
        x=q.front().x ,y=q.front().y ;
        q.pop();
        for(int i=0;i<4;++i)
        {
            dx=x+d1[i] ,dy=y+d2[i] ;
            if(dx<1||dy<1||dx>n||dy>m)continue;
            if(map[dx][dy]&&(dx!=xb||dy!=yb)&&dis[dx][dy]==inf)
            {
                dis[dx][dy]=dis[x][y]+1 ;
                q.push((aaa){dx,dy});
            }
        }
    }
}

queue<node>point;

int solve()
{
    if(xb==xe&&yb==ye)return 0;
    pre();
    memset(tmp,inf,sizeof tmp);

    int dx ,dy ;
    // tmp[x][y][k]保存点(x,y)到k方向的相邻点的最短路
    for(int i=0;i<4;++i)
    {
        dx=xb+d1[i] ,dy=yb+d2[i] ;
        if(dx<1||dy<1||dx>n||dy>m)continue;
        if(map[dx][dy]&&dis[dx][dy]!=inf)
        {
            tmp[xb][yb][i]=dis[dx][dy];
            point.push((node){xb,yb,i});
            in[xb][yb][i]=1;
        }
    }

    int x ,y ,k ;
    while(!point.empty())
    {
        x=point.front().x ,y=point.front().y ,k=point.front().k ;
        point.pop();
        in[x][y][k]=0;
        dx=x+d1[k] ,dy=y+d2[k] ;

        //将点(x,y)移到空格处
        if(tmp[dx][dy][(k+2)%4]>tmp[x][y][k]+1)
        {
            tmp[dx][dy][(k+2)%4]=tmp[x][y][k]+1;
            if(!in[dx][dy][(k+2)%4])
            {
                in[dx][dy][(k+2)%4]=1;
                point.push((node){dx,dy,(k+2)%4});
            }
        }
        //空格(也与点(x,y)相邻)移动到点(x,y)相邻的格子去
        for(int i=0;i<4;i++)
        {
            dx=x+d1[i] ,dy=y+d2[i] ;
            if(dx>0&&dy>0&&dx<=n&&dy<=m&&map[dx][dy]&&i!=k&&step[x][y][k][i]!=inf)
            {
                if(tmp[x][y][i]>tmp[x][y][k]+step[x][y][k][i])
                {
                    tmp[x][y][i]=tmp[x][y][k]+step[x][y][k][i];
                    if(!in[x][y][i])
                    {
                        in[x][y][i]=1;
                        point.push((node){x,y,i});
                    }
                }
            }
        }
    }

    int ans=inf;
    for(int i=0;i<4;++i)
        ans=min(ans,tmp[xe][ye][i]);
    if(ans==inf)return -1;
    return ans;
}

int main()
{
    int T ;
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            scanf("%d",&map[i][j]);
    memset(step,inf,sizeof(step));
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            if(map[i][j])
                init(i,j);
    while(T--)
    {
        scanf("%d%d%d%d%d%d",&x0,&y0,&xb,&yb,&xe,&ye);
        printf("%d\n",solve());
    }
    return 0;
}

你可能感兴趣的:(华容道,NOIp2013)