zoukankan      html  css  js  c++  java
  • 华容道

    【问题描述】

    小B最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。

    小B玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

    (1)在一个n*m棋盘上有n*m个格子,其中有且只有一个格子是空白的,其余n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是1*1的;

    (2)有些棋子是固定的,有些棋子则是可以移动的;

    (3)任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上;

    游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

    给定一个棋盘,游戏可以玩q次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第i次玩的时候,空白的格子在第EXi行第EYi列,指定的可移动棋子的初始位置为第SXi行第SYi列,目标位置为第TXi行第TYi列。

    假设小B每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小B每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

    【输入描述】

    第一行有3个整数,每两个整数之间用一个空格隔开,依次表示n、m和q;

    接下来的n行描述一个n*m的棋盘,每行有m个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0表示该格子上的棋子是固定的,1表示该格子上的棋子可以移动或者该格子是空白的。接下来的q行,每行包含6个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

    【输出描述】

    输出有q行,每行包含1个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。

    【输入样例】

    3 4 2

    0 1 1 1

    0 1 1 0

    0 1 0 0

    3 2 1 2 2 2

    1 2 2 2 3 2

    【输出样例】

    2

    -1

    【数据范围及提示】

    棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

    (1)第一次游戏,空白格子的初始位置是(3,2)(图中空白所示),游戏的目标是将初始位置在(1,2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2,2)(图中红色的格子)上。

    移动过程如下:

    (2)第二次游戏,空白格子的初始位置是(1,2)(图中空白所示),游戏的目标是将初始位置在(2,2)上的棋子(图中绿色圆圈所示)移动到目标位置(3,2)上。

    要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无法完成。

    对于30%的数据,1 ≤ n,m ≤ 10,q = 1;

    对于60%的数据,1 ≤ n,m ≤ 30,q ≤ 10;

    对于100%的数据,1 ≤ n,m ≤ 30,q ≤ 500。

    连黄学长都弃坑了,本蒟蒻还能干什么(70分BFS):

    源代码:
    
    #include<cstdio>
    #include<cstring>
    int n,m,P;
    bool Map[31][31],Mark[31][31][31][31];
    int x[4]={0,0,1,-1},y[4]={1,-1,0,0};
    struct Node
    {
        int EX,EY,SX,SY,Sum;
    }Q[810001]={0}; //坑爹1者也,不要傻傻地相信Windows。
    bool Judge(int X,int Y,int T1,int T2)
    {
        if (T1<1||T2<1||T1>n||T2>m||!Map[T1][T2]) //坑爹2者也,注意在Search()中,空白点与指定点已交换位置。
          return false;
        if (Mark[X][Y][T1][T2]) //已经有过此种情况的移动,那么再怎么移动不过白费力气,舍去。
          return false;
        Mark[X][Y][T1][T2]=true;
        return true;
    }
    void Search()
    {
        memset(Mark,0,sizeof(Mark)); //经过灵活严谨的题目分析,可得Mark[X1][Y1][X2][Y2]状态是唯一的,值得学习借鉴。
        int Head(0),Tail(0);
        int X,Y,T1,T2,TX,TY;
        scanf("%d%d%d%d%d%d",&Q[0].EX,&Q[0].EY,&Q[0].SX,&Q[0].SY,&TX,&TY);
        if (TX==Q[0].SX&&TY==Q[0].SY) //特判。
        {
            printf("0
    ");
            return;
        }
        Mark[Q[0].SX][Q[0].SY][Q[0].EX][Q[0].EY]=true; //表示已经遍历过此情况。
        while (Head<=Tail) //BFS。
        {
            for (int a=0;a<4;a++)
            {
                X=Q[Head].SX;
                Y=Q[Head].SY; //指定块的位置。
                T1=Q[Head].EX+x[a];
                T2=Q[Head].EY+y[a]; //空白块移动。
                if (X==T1&&Y==T2) //如果空白块能移动到指定块,移动指定块到空白块。
                { 
                    X=Q[Head].EX;
                    Y=Q[Head].EY;
                }
                if (Judge(X,Y,T1,T2)) //如果该状态符合条件,入队。
                {
                    Tail++;
                    Q[Tail].SX=X;
                    Q[Tail].SY=Y;
                    Q[Tail].EX=T1;
                    Q[Tail].EY=T2;
                    Q[Tail].Sum=Q[Head].Sum+1;
                    if (X==TX&&Y==TY) //如果到达指定位置输出移动步数即时间。
                    {
                        printf("%d
    ",Q[Tail].Sum);
                        return;
                    }
                }
            }
            Head++;
        }
        printf("-1
    ");
        return;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&P);
        for (int a=1;a<=n;a++)
          for (int b=1;b<=m;b++)
            scanf("%d",&Map[a][b]);
        for (int a=1;a<=P;a++)
          Search();
        return 0;
    }

    正解(BFS+SPFA):

    源代码:
    
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<stack>
    #define INF 0x3f3f3f3f
    using namespace std;
    struct Node1
    {
      int X,Y;
    };
    struct Node2
    {
      int X,Y,Now;
    };
    queue <Node1> Q1;
    queue <Node2> Q2;
    stack <Node2> st;
    int m,n,P,EX,EY,SX,SY,TX,TY;
    int Map[31][31],i[31][31],Step[31][31][5][5],inq[31][31][5],DP[31][31][4];
    int x[4]={0,1,0,-1},y[4]={1,0,-1,0};
    void Pre_BFS(int X,int Y) //预处理出(sx,sy)这个格子周围四个格子间不经过(X,Y)的最短路。
    {
        for (int a=0;a<4;a++)
        {
            int T1=X+x[a]; 
            int T2=Y+y[a];
            if (T1>0&&T2>0&&T1<=n&&T2<=m&&Map[T1][T2])
                st.push((Node2){T1,T2,a});
        }
        while (!st.empty()) //需要一个一个处理。
        {
            while (!Q1.empty())
              Q1.pop();
            memset(i,INF,sizeof(i));
            int T1=st.top().X;
            int T2=st.top().Y;
            int Now=st.top().Now;
            i[T1][T2]=0;
            Q1.push((Node1){T1,T2});
            st.pop();
            while (!Q1.empty())
            {
                Node1 T=Q1.front();
                Q1.pop();
                int XXX=T.X;
                int YYY=T.Y;
                for (int a=0;a<4;a++)
                {
                    int S1=XXX+x[a];
                    int S2=YYY+y[a];
                    if (S1>0&&S2>0&&S1<=n&&S2<=m&&Map[S1][S2]&&(S1!=X||S2!=Y)&&i[S1][S2]==INF) //由于路权都为1,所以第一次到达即为最短路。
                    {
                        i[S1][S2]=i[XXX][YYY]+1;
                        Q1.push((Node1){S1,S2});
                    }
                }
            }
            for (int a=0;a<4;a++)
            {
                int S1=X+x[a];
                int S2=Y+y[a];
                if (S1>0&&S2>0&&S1<=n&&S2<=m&&Map[S1][S2]&&(S1!=T1||S2!=T2)&&i[T1][T2]!=INF)
                  Step[X][Y][Now][a]=i[S1][S2]; //记录最短路。
            }
        }
    }
    void Pre_Black() //找当前的空格到棋子周围的空格的最短路。
    {
        while (!Q1.empty())
          Q1.pop();
        memset(i,0x3f,sizeof(i));
        i[EX][EY]=0;
        Q1.push((Node1){EX,EY});
        while(!Q1.empty())
        {
            Node1 T=Q1.front();
            Q1.pop();
            for (int a=0;a<4;a++)
            {
                int T1=T.X+x[a];
                int T2=T.Y+y[a];
                if (T1>0&&T2>0&&T1<=n&&T2<=m&&Map[T1][T2]&&(T1!=SX||T2!=SY)&&i[T1][T2]==INF) //不能经过(SX,SY)这个点。
                {
                    i[T1][T2]=i[T.X][T.Y]+1;
                    Q1.push((Node1){T1,T2});
                }
            }
        }
    }
    int BFS() //个人觉得叫SPFA()更合适些。
    {
        if (TX==SX&&TY==SY)
          return 0; //起点与终点重合,返回0。
        Pre_Black(); //预处理。
        memset(DP,INF,sizeof(DP));
        while (!Q2.empty())
          Q2.pop();
        for (int a=0;a<4;a++)
        {
            int T1=SX+x[a];
            int T2=SY+y[a];
            if (T1>0&&T2>0&&T1<=n&&T2<=m&&i[T1][T2]!=INF&&Map[T1][T2])
            {
                DP[SX][SY][a]=i[T1][T2]; //加入队列。
                Q2.push((Node2){SX,SY,a});
                inq[SX][SY][a]=1;
            }
        }
        while(!Q2.empty())
        {
            Node2 T=Q2.front();
            Q2.pop();
            inq[T.X][T.Y][T.Now]=0; //棋子移到空格的情况。
            int T1=T.X+x[T.Now];
            int T2=T.Y+y[T.Now];
            if (DP[T1][T2][(T.Now+2)%4]>DP[T.X][T.Y][T.Now]+1)
            {
                DP[T1][T2][(T.Now+2)%4]=DP[T.X][T.Y][T.Now]+1;
                if (!inq[T1][T2][(T.Now+2)%4])
                {
                    inq[T1][T2][(T.Now+2)%4]=1;
                    Q2.push((Node2){T1,T2,(T.Now+2)%4});
                }
            } //棋子周围的空格互相移动的情况。
            for (int a=0;a<4;a++)
            {
                T1=T.X+x[a];
                T2=T.Y+y[a];
                if (T1>0&&T2>0&&T1<=n&&T2<=m&&Map[T1][T2]&&a!=T.Now&&Step[T.X][T.Y][T.Now][a]!=INF)
                  if (DP[T.X][T.Y][a]>DP[T.X][T.Y][T.Now]+Step[T.X][T.Y][T.Now][a])
                  {
                    DP[T.X][T.Y][a]=DP[T.X][T.Y][T.Now]+Step[T.X][T.Y][T.Now][a];
                    if (!inq[T.X][T.Y][a])
                    {
                        inq[T.X][T.Y][a]=1;
                        Q2.push((Node2){T.X,T.Y,a});
                    }
                  }
            }
        }
        int ans=INF;
        for (int a=0;a<4;a++) //找最短路。
          ans=min(ans,DP[TX][TY][a]);
        if (ans==INF)
          return -1; //不存在。
        return ans;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&P);
        for (int a=1;a<=n;a++)
          for(int b=1;b<=m;b++)
            scanf("%d",&Map[a][b]);
        memset(Step,INF,sizeof(Step));
        for (int a=1;a<=n;a++)
          for (int b=1;b<=m;b++)
            if (Map[a][b])
              Pre_BFS(a,b); //预处理。
        for (int a=1;a<=P;a++)
        {
            scanf("%d%d%d%d%d%d",&EX,&EY,&SX,&SY,&TX,&TY);
            printf("%d
    ",BFS());
        }
        return 0;
    }
  • 相关阅读:
    implement the bucket sort and some analysis
    冪運匴
    polynomial multiplication
    WinRAR(WinZip)压缩与解压实现(C#版Window平台)
    Windows 窗体计时器事件,则不引发在 Windows 服务
    VS 2008智能提示是英文的怎么改成中文的?
    C#_SQL数据访问帮助类
    C#_DllImport用法和路径问题
    Office2003/2007/2010强力卸载工具下载
    Windows_装的是2008R2系统,在电脑属性里显示内存12G(可用4G)
  • 原文地址:https://www.cnblogs.com/Ackermann/p/5787001.html
Copyright © 2011-2022 走看看