noip 2013 华容道

玄学的搜索与建图...

首先经过分析,可以得出几个结论:

①.移动棋子等价于移动空格

原因:如果一个棋子可以移动,那么这个棋子一定是和旁边的空格交换了位置,所以也就相当于是移动空格

②.有价值的图的状态是有限的

原因:我们的目标是把起始棋子移到结束位置上,那么如果能够移动起始棋子,一定是把空格移到了起始位置旁边,那么根据结论①,我们只需把这个状态转移到空格子在目标位置附近就完成任务了!

那么根据上述结论,我们可以构造一个状态num[i][j][k],表示空格在点(i,j)的哪一方向(第三维用四个数字代表上下左右)

这个状态数是很少的。

接下来,我们只需考虑转移即可。

我们发现,比如我们想从某一状态num[i][j][k]转移到另一状态num[i][j]k`](注意i,j不变),那么首先,点i,j不能动,这样进行一个bfs,就可以求出代价,然后把这两个状态连边

当然,也存在一种情况就是直接交换空格和i,j,但这样就是由状态num[i][j][k]变成了状态num[i'][j'][k'],其中i' j'是这个点周围的三个点(显然要除掉空格),而k'是这个点i,j相对于原三个点的位置(上下左右),这样的代价是1,同样连边即可

最后对所有状态跑一遍最短路spfa,要求初始状态是空格在起点四周,通过bfs求出初值,终止状态是空格在终点四周,通过最短路由初状态更新即可

注意如果最短路始终为正无穷即为无解

上代码(自认为全站最好看,0压行)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct Edge
{
    int next;
    int to;
    int val;
}edge[300005];
int head[3005];
int cnt=1;
struct node
{
    int x,y,d;
};
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void add(int l,int r,int w)
{
    edge[cnt].next=head[l];
    edge[cnt].to=r;
    edge[cnt].val=w;
    head[l]=cnt++;
}
int n,m,q;
int maps[35][35];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int num[35][35][4];
bool flag[35][35];
int dis[50005];
int sx,sy,ex,ey,bx,by;
int bfs(int stx,int sty,int edx,int edy,int rtx,int rty)
{
    if(stx==edx&&sty==edy)
    {
        return 0;
    }
    memset(flag,0,sizeof(flag));
    queue <node> M;
    node u0;
    u0.x=stx;
    u0.y=sty;
    u0.d=0;
    M.push(u0);
    while(!M.empty())
    {
        node u=M.front();
        M.pop();
        int x=u.x;
        int y=u.y;
        int d=u.d;
        for(int i=0;i<=3;i++)
        {
            int xx=x+dir[i][0];
            int yy=y+dir[i][1];
            if(!maps[xx][yy])
            {
                continue;
            }
            if(xx==rtx&&yy==rty)
            {
                continue;
            }
            if(flag[xx][yy])
            {
                continue;
            }
            if(xx==edx&&yy==edy)
            {
                return d+1;
            }
            flag[xx][yy]=1;
            node tt;
            tt.x=xx;
            tt.y=yy;
            tt.d=d+1;
            M.push(tt);
        }
    }
    return 0x3f3f3f3f;
}
int spfa()
{
    if(sx==ex&&sy==ey)
    {
        return 0;
    }
    memset(dis,0x3f,sizeof(dis));
    queue <int> M;
    for(int i=0;i<=3;i++)
    {
        int x=sx+dir[i][0];
        int y=sy+dir[i][1];
        if(num[sx][sy][i])
        {
            dis[num[sx][sy][i]]=bfs(bx,by,x,y,sx,sy);
            M.push(num[sx][sy][i]);
        }
    }
    while(!M.empty())
    {
        int u=M.front();
        M.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dis[to]>dis[u]+edge[i].val)
            {
                dis[to]=dis[u]+edge[i].val;
                M.push(to);
            }
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=0;i<=3;i++)
    {
        if(num[ex][ey][i])
        {
            ans=min(ans,dis[num[ex][ey][i]]);
        }
    }
    if(ans==0x3f3f3f3f)
    {
        return -1;
    }else
    {
        return ans;
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    init();
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&maps[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(int k=0;k<=3;k++)
            {
                int x=i+dir[k][0];
                int y=j+dir[k][1];
                if(maps[i][j]&&maps[x][y])
                {
                    num[i][j][k]=++tot;
                }
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(int k=0;k<=3;k++)
            {
                if(num[i][j][k])
                {
                    int x=i+dir[k][0];
                    int y=j+dir[k][1];
                    add(num[i][j][k],num[x][y][k^1],1);//交换空点和旁边的点 
                }
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(int k=0;k<=3;k++)
            {
                for(int t=0;t<=3;t++)
                {
                    if(k!=t&&num[i][j][k]&&num[i][j][t])
                    {
                        int x1=i+dir[k][0];
                        int y1=j+dir[k][1];
                        int x2=i+dir[t][0];
                        int y2=j+dir[t][1];
                        add(num[i][j][k],num[i][j][t],bfs(x1,y1,x2,y2,i,j));
                    }
                }
            }
        }
    }
    while(q--)
    {
        scanf("%d%d%d%d%d%d",&bx,&by,&sx,&sy,&ex,&ey);
        printf("%d
",spfa());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zhangleo/p/10764228.html