• 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;
    }
  • 相关阅读:
    空间解析几何与向量代数(复习笔记)
    OpenGL数据类型
    Symbian c++ 调用标准C产生内存泄露
    symbian c++调用标准c函数方法
    (转载)一篇对理解OpenGL的描述的文章
    (个人摘要)make工具的用法
    (转载)哈佛大学凌晨4点半的景象
    网页抓取中的debug 问题记录
    split 使用
    三、抽象工厂(Abstract Factory)模式
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10764228.html
Copyright © 2020-2023  润新知