Abbott's Revenge 题解

    The 1999 World Finals Contest included a problem based on a dice maze. At the time the problemwas written, the judges were unable to discover the original source of the dice maze concept. Shortlyafter the contest, however, Mr. Robert Abbott, the creator of numerous mazes and an author on thesubject, contacted the contest judges and identified himself as the originator of dice mazes. We regretthat we did not credit Mr. Abbott for his original concept in last years problem statement. But we arehappy to report that Mr. Abbott has offered his expertise to this years contest with his original andunpublished walk-through arrow mazes.

    As are most mazes, a walk-through arrow maze is traversed by moving from intersection to intersectionuntil the goal intersection is reached. As each intersection is approached from a given direction,a sign near the entry to the intersection indicates in which directions the intersection can be exited.These directions are always left, forward or right, or any combination of these.

    Figure 1 illustrates a walk-through arrow maze. The intersections are identified as (row, column)pairs, with the upper left being (1,1). The Entrance intersection for Figure 1 is (3,1), and the Goalintersection is (3,3). You begin the maze by moving north from (3,1). As you walk from (3,1) to (2,1),the sign at (2,1) indicates that as you approach (2,1) from the south (traveling north) you may continueto go only forward. Continuing forward takes you toward (1,1). The sign at (1,1) as you approach fromthe south indicates that you may exit (1,1) only by making a right. This turns you to the east nowwalking from (1,1) toward (1,2). So far there have been no choices to be made. This is also the case asyou continue to move from (1,2) to (2,2) to (2,3) to (1,3). Now, however, as you move west from (1,3)toward (1,2), you have the option of continuing straight or turning left. Continuing straight would takeyou on toward (1,1), while turning left would take you south to (2,2). The actual (unique) solution tothis maze is the following sequence of intersections: (3,1) (2,1) (1,1) (1,2) (2,2) (2,3) (1,3) (1,2) (1,1)(2,1) (2,2) (1,2) (1,3) (2,3) (3,3).

    You must write a program to solve valid walk-through arrow mazes. Solving a maze means (ifpossible) finding a route through the maze that leaves the Entrance in the prescribed direction, andends in the Goal. This route should not be longer than necessary, of course.

Input

    The input file will consist of one or more arrow mazes. The first line of each maze description containsthe name of the maze, which is an alphanumeric string of no more than 20 characters. The next linecontains, in the following order, the starting row, the starting column, the starting direction, the goalrow, and finally the goal column. All are delimited by a single space. The maximum dimensions ofa maze for this problem are 9 by 9, so all row and column numbers are single digits from 1 to 9.The starting direction is one of the characters N, S, E or W, indicating north, south, east and west,respectively.

    All remaining input lines for a maze have this format: two integers, one or more groups of characters,and a sentinel asterisk, again all delimited by a single space. The integers represent the row and column,respectively, of a maze intersection. Each character group represents a sign at that intersection. Thefirst character in the group is ‘N’, ‘S’, ‘E’ or ‘W’ to indicate in what direction of travel the sign wouldbe seen. For example, ‘S’ indicates that this is the sign that is seen when travelling south. (This is thesign posted at the north entrance to the intersection.) Following this first direction character are oneto three arrow characters. These can be ‘L’, ‘F’ or ‘R’ indicating left, forward, and right, respectively

    The list of intersections is concluded by a line containing a single zero in the first column. The nextline of the input starts the next maze, and so on. The end of input is the word ‘END’ on a single lineby itself.

Output

    Robert Abbotts walk-through arrowmazes are actually intended for large-scaleconstruction, not paper. Although hismazes are unpublished, some of them haveactually been built. One of these is on displayat an Atlanta museum. Others havebeen constructed by the American MazeCompany over the past two summers. Astheir name suggests these mazes are intendedto be walked through.

    For the adventurous, Figure 2 agraphic of Robert Abbotts Atlanta maze.Solving it is quite difficult, even whenyou have an overview of the entire maze.Imagine trying to solve this by actuallywalking through the maze and only seeingone sign at a time! Robert Abbott himselfindicated that the maze is too complexand most people give up before finishing.Among the people that did notgive up was Donald Knuth: it took himabout thirty minutes to solve the maze.

The first maze in the following sample input is the mazein Figure 1.



Sample Input

SAMPLE

3 1 N 3 3

1 1 WL NR *

1 2 WLF NR ER *

1 3 NL ER *

2 1 SL WR NF *

2 2 SL WF ELF *

2 3 SFR EL *

0

NOSOLUTION

3 1 N 3 21 1 WL NR *

1 2 NL ER *

2 1 SL WR NFR *

2 2 SR EL *

0

END

Sample Output

SAMPLE

  (3,1) (2,1) (1,1) (1,2) (2,2) (2,3) (1,3) (1,2) (1,1) (2,1)

  (2,2) (1,2) (1,3) (2,3) (3,3)

NOSOLUTIONNo 

  Solution Possible



题意处理转向问题的状态广搜,就是给了9*9的格子,然后给了起点和终点,还有起始朝向(面朝哪),问最短路的路径。输入的话,先是一个坐标,然后是在这个坐标点,面朝哪个方向(N,W,S,E)后跟可以转向的情况(F,L ,R),由于一个路口,最多可以有4个朝向,每个朝向最多可以跟三种状态。由于条件都是输入给的,也就是说只有输入给的数据可以用!没有提到的点不能走;但提到的点,如果给了一个朝向,一种转弯状态,那就只能用这一种!!比如 (X,Y)  NL 在这个点如果面朝北的话,可以左转,  WF面朝西的话可以直行,,再次强调,没有给的状态不能走!!!

思路一个点“最多”可以有4种朝向,每个朝向可以有三种转弯状态,也就是每个点可以有12种状态。那么可以开一个四维数组保存每一种状态   str[x][y][face][zw]; ,face为朝向,zw(转弯)为可以向哪转;  同时处理转弯的时候,可以根据其朝向,和三种转向方式,

可以写出 dis[4][3][2]= {-1,0,0,-1,0,1,   0,1,-1,0,1,0,   1,0,0,1,0,-1,   0,-1,1,0,-1,0};//dis[面朝哪][这个朝向的直行,左右转的x,y的变化][x,y]   转向后的方向 dis1[4][3]= {0,3,1, 1,0,2, 2,1,3, 3,2,0};  处理好转向问题的话,接下来进行一个广搜就好了,这道题问的是路径,然后我就模拟队列,最后在开一个2维数组存路径的坐标!


最大坑点:起点和终点在同一个地点的时候,不能直接输出起点,要看看起点的的下一个位置,能不能再次回到起点!!!!神坑!!     还有注意处理好转向问题,包括转过去的坐标,转过去的方向!


代码如下:

/*
规定方向:


N(0)           F(0)
W(3)    E(1)       L(1)  R(2)
S(2)   

*/

#include<cstdio>
#include<cstring>   
#include<queue>
using namespace std;

//str[x][y][面朝哪][可直行?左,右转?] 

//book为三维,标记这一点的四个方向是否走过 

int n,m,ex,ey,tx,ty,minn,flag,str[20][20][20][20],book[20][20][20],num[10010][2];

//dis[面朝哪][这个朝向的直行,左右转的x,y的变化][x,y] 

int dis[4][3][2]= {-1,0,0,-1,0,1,  0,1,-1,0,1,0,  1,0,0,1,0,-1,  0,-1,1,0,-1,0};

int dis1[4][3]= {0,3,1, 1,0,2, 2,1,3, 3,2,0};//转过去后,面朝哪? 
struct node
{
    int x,y,find,step,m;  //find 为面朝呐个方向,step步数,m是 从 list[m]这个点搜到此时这个点的 
} list[10010];

void bfs(int x,int y,int find)//模拟队列 
{
    int i,j,h=1,e=2;  
    list[1].x=x;		
    list[1].y=y;
    list[1].step=1;
    list[1].find=find;
    list[1].m=0;
    while(h<e)
    {
        int x1=list[h].x;               //(x1,y1)此时点的坐标 
        int y1=list[h].y;
        int nn=list[h].find;            //nn为此时的方向,为了方便往数组里写 
        for(i=0; i<3; i++)              //三个转向 0直行 1左转 2右转 
        {
            if(str[x1][y1][nn][i])      //如果这个点 是面朝nn的,此时可以往哪个方向转? 
            {
            	int th;                 //th为往i方向转过去后的 朝向,也是面朝 th 方向 
				th=dis1[nn][i]; 
            	if(book[x1][y1][th])    //若(x1,y1)面朝th 已经走过了,就不加入队列了 
                	continue;
                book[x1][y1][th]=1;     //标记这个朝向已经用过了 
                int xx=x1+dis[nn][i][0]  //下一个位置(xx,yy),面朝th 
                int yy=y1+dis[nn][i][1];
                int tt=dis1[nn][i];
                if(xx<1||xx>9||yy<1||yy>9)continue;    //越界的话,跳过 
                
                list[e].x=xx;                 //入队列 
                list[e].y=yy;
                list[e].step=list[h].step+1;
                list[e].find=tt;
                list[e].m=h;        
				      
                if(xx==tx&&yy==ty)            // 找到终点了 
                {
                    flag=1;
                    int q=list[e].step;       //走了多少步,倒序加入2维数组 
                    minn=q;                   //全局变量,记录步数,起点为 0 
                    int p,g=e;
                    num[q][0]=xx;            //起点加入 
                    num[q][1]=yy;
                    while(list[g].m>=0)      //依次得到路径 
                    {
                        p=list[g].m;
                        q--;
                        num[q][0]=list[p].x;
                        num[q][1]=list[p].y;
                        g=p;
                    }
                    return;
                }
                e++;
            }
        }
        h++;
    }
}

int main()
{
    char c,s[110],t[35];
    int a,b,i,j;
    while(~scanf("%s",s))
    {
        if(strcmp(s,"END")==0)break;
        
        flag=0;
        memset(str,0,sizeof(str));
        memset(book,0,sizeof(book));
        
        scanf("%d %d %c %d %d",&ex,&ey,&c,&tx,&ty);


        while(scanf("%d",&a)&&a)
        {
            scanf("%d",&b);
            while(scanf("%s",t))
            {
                if(strcmp(t,"*")==0)break;
                int l=strlen(t);
                int n1;
                if(t[0]=='N')n1=0;               //这个条件下的朝向 
                else if(t[0]=='E')n1=1; 
                else if(t[0]=='S')n1=2;
                else n1=3;
				
                for(i=1; i<l; i++)            //这一点在这个朝向的三种转向 
                {
                    int n2;
                    if(t[i]=='F')n2=0;
                    else if(t[i]=='L')n2=1;
                    else n2=2;
                    
                    str[a][b][n1][n2]=1;     //(x,y)面朝n1,可以往 n2转 
                }
            }
        }

        list[0].x=ex;                     //起点放入第一个结构体里  
        list[0].y=ey;
        list[0].m=-1;                    //它的根为-1,作为结束条件
        int xx,yy,n1;
		if(c=='N')n1=0;         //这个条件下的朝向 
        else if(c=='E')n1=1; 
        else if(c=='S')n1=2;
        else n1=3;
        xx=ex+dis[n1][0][0];           //得到下一步的位置(xx,yy)和状态朝向n1 
        yy=ey+dis[n1][0][1];
		
        printf("%s\n  ",s);
        if(xx==tx&&yy==ty)            //如果下一步就是终点 
        {
            printf("(%d,%d) (%d,%d)\n",ex,ey,tx,ty);
            continue;
        }


        bfs(xx,yy,n1);   
		                       //格式化输出 
        if(flag)
        {
            if(minn<=9)                //小于10步的话  因为下标从0开始 ,         
			{
                for(i=0; i<=minn; i++)
                {
                    if(i<minn)
                        printf("(%d,%d) ",num[i][0],num[i][1]);
                    else
                        printf("(%d,%d)\n",num[i][0],num[i][1]);
                }
            }
            else
            {
                for(i=0; i<=minn; i++)    
                {
                    if(i&&(i%9==0))        // 输出十个,换行,然后开头空两格
                    {
                        printf("(%d,%d)",num[i][0],num[i][1]);
                        printf("\n  ");
                        if(minn-i<=10)       //判断剩下的够不够10个
                            break;
                    }
                    else
                        printf("(%d,%d) ",num[i][0],num[i][1]);
                }
                for(i=i+1; i<=minn; i++)
                {
                    if(i<minn)
                        printf("(%d,%d) ",num[i][0],num[i][1]);
                    else
                        printf("(%d,%d)\n",num[i][0],num[i][1]);
                }
            }
        }
        else
            printf("No Solution Possible\n");
    }
}


猜你喜欢

转载自blog.csdn.net/qq_41890797/article/details/80561994