问题描述
在1999年的World Finals中有一个基于Dice Maze(骰子迷宫)的题目。在出题人编写那道题目的时候,他们并没有发现这种迷宫的创意来源。然而在那场比赛结束不久,创作了大量的这种迷宫的Robert Abbott先生,联系了比赛主办方并确认自己是骰子迷宫的原作者。我们很遗憾没有在去年的题目描述中感谢Abbott先生的原创意,但我们很高兴Abbott先生主动为今年的比赛提供他原创的、未公开的“步行通过的箭头迷宫”。
与大多数的迷宫相同,箭头迷宫也是每次从一个路口走到另一个路口,直到到达终点。在每个路口处,如果你从某个方向进入了该路口,那么路口的地面上在靠近你的方向会画有一组箭头,它们相对于你的方向可以是向左,向前,向右,或者是它们的任意组合。
图1描述了一个箭头迷宫。每个路口用二维坐标(x,y)表示,以左上角的路口为(1,1)。在图1给出的迷宫中,起点的坐标是(3,1),终点的坐标是(3,3)。在你开始后,你只能向北走1步到达(2,1)。由于你是从南边到达(2,1)这一点的,而这一点在南边的箭头是向前指的,所以你只能继续向前走到达(1,1)。在此之后,由于你是从南边到达了(1,1),这一点在南边的箭头是向右指的,所以你也只能向右拐,到达(1,2)。到目前为止,你还没有做出任何选择。以此类推,你会接着依次经过(2,2) (2,3) (1,3) (1,2)。现在你可以选择继续向前走或者向左转。如果你向前走,你会回到(1,1),而向左转可以让你到达(2,2)。事实上,唯一最优的方案是依次经过以下路口:(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)。
你需要写一个程序解决这个迷宫。“解决”的意义是:只要迷宫是可解的,你就要找到一条路线,它必须在起点沿指定的方向走出,并最终到达终点,当然,路线的长度需要是所有方案中最短的。
与大多数的迷宫相同,箭头迷宫也是每次从一个路口走到另一个路口,直到到达终点。在每个路口处,如果你从某个方向进入了该路口,那么路口的地面上在靠近你的方向会画有一组箭头,它们相对于你的方向可以是向左,向前,向右,或者是它们的任意组合。
图1描述了一个箭头迷宫。每个路口用二维坐标(x,y)表示,以左上角的路口为(1,1)。在图1给出的迷宫中,起点的坐标是(3,1),终点的坐标是(3,3)。在你开始后,你只能向北走1步到达(2,1)。由于你是从南边到达(2,1)这一点的,而这一点在南边的箭头是向前指的,所以你只能继续向前走到达(1,1)。在此之后,由于你是从南边到达了(1,1),这一点在南边的箭头是向右指的,所以你也只能向右拐,到达(1,2)。到目前为止,你还没有做出任何选择。以此类推,你会接着依次经过(2,2) (2,3) (1,3) (1,2)。现在你可以选择继续向前走或者向左转。如果你向前走,你会回到(1,1),而向左转可以让你到达(2,2)。事实上,唯一最优的方案是依次经过以下路口:(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)。
你需要写一个程序解决这个迷宫。“解决”的意义是:只要迷宫是可解的,你就要找到一条路线,它必须在起点沿指定的方向走出,并最终到达终点,当然,路线的长度需要是所有方案中最短的。
输入格式
输入文件包括一个或多个箭头迷宫。每个迷宫描述的第一行是这个迷宫的名称,保证它只由数字和字母组成且长度不超过20。接下来的一行依次包含了起点的坐标,起始时方向,目标点的坐标,以空格隔开。本题的迷宫最大尺寸为9×9,所以行与列的编号均为1到9。起始时的方向为N,S,E,W之一,分别代表北、南、西、东。
剩下的若干行按照以下格式输入:一对整数,若干字符串,以星号(*)结束,以空格隔开。每一行代表一个路口,一对整数表示路口的坐标。对于每一个字符串,第一位为N,S,E,W之一,接下来若干位只可能包含L,F,R,分别代表向左,向前,向右。这个字符串的含义是:朝向某个方向进入该路口(所以箭头被画在这个路口的相反方向),接下来就只能向某个方向继续行走。
对于每个迷宫,以0作为一行的结束,从接下来一行开始就是一个新的箭头迷宫。输入文件以单独的一行END作为结尾。
剩下的若干行按照以下格式输入:一对整数,若干字符串,以星号(*)结束,以空格隔开。每一行代表一个路口,一对整数表示路口的坐标。对于每一个字符串,第一位为N,S,E,W之一,接下来若干位只可能包含L,F,R,分别代表向左,向前,向右。这个字符串的含义是:朝向某个方向进入该路口(所以箭头被画在这个路口的相反方向),接下来就只能向某个方向继续行走。
对于每个迷宫,以0作为一行的结束,从接下来一行开始就是一个新的箭头迷宫。输入文件以单独的一行END作为结尾。
输出格式
对于每个箭头迷宫,应该先输出它的名字,然后接下来若干行,输出一个路径,格式如问题描述中所述;或者输出"No Solution Possible"。迷宫的名字应从第1列开始,而其余所有的行都从第3列开始,即行首有2个空格。对于输出的每个路径,除最后一行外,每行须输出恰好10个路口(坐标)。
在下面的样例中,输入的第一个迷宫是图1中的迷宫。
注意,在下面的样例输出当中,除了SAMPLE和NOSOLUTION两行以外,其余的行首都需要空两格!(由于格式化问题未能显示出来)
在下面的样例中,输入的第一个迷宫是图1中的迷宫。
注意,在下面的样例输出当中,除了SAMPLE和NOSOLUTION两行以外,其余的行首都需要空两格!(由于格式化问题未能显示出来)
样例输入
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 2
1 1 WL NR *
1 2 NL ER *
2 1 SL WR NFR *
2 2 SR EL *
0
END
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 2
1 1 WL NR *
1 2 NL ER *
2 1 SL WR NFR *
2 2 SR EL *
0
END
样例输出
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)
NOSOLUTION
No Solution Possible
(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)
NOSOLUTION
No Solution Possible
数据规模和约定
设T为数据组数:对于30%的数据,T=1;对于60%的数据,T≤100;对于100%的数据,T≤1000。
算法是简单的BFS,就是数据处理较为麻烦。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int mapp[10][10];
//标记从某点出来向某个方向
bool vis[10][10][5];
//存储每个坐标四个方向的转弯情况
struct guide{
bool dir[5][4];
guide(){
for(int i=0;i<5;i++){
for(int j=0;j<4;j++)
dir[i][j]=0;
}
}
};
//记录路径和过程中经历的点
struct node{
int x,y; //坐标
string road; //路径
node(){
road.clear();
}
};
queue<node>qu;
//1,2,3,4分别代表东、西、南、北
int c_to_num(char ch){
if(ch=='E') return 1;
else if(ch=='W') return 2;
else if(ch=='S') return 3;
else return 4;
}
//1,2,3分别代表向左、向前、向右
int g_to_num(char ch){
if(ch=='L') return 1;
else if(ch=='F') return 2;
else return 3;
}
bool judge(node now){
if(now.x>0&&now.x<=9&&now.y>0&&now.y<=9&&!vis[now.x][now.y][now.road[now.road.size()-1]-'0'])
return true;
return false;
}
string bfs(int r,int s,guide *p){
while(!qu.empty()){
node now=qu.front(),next;
qu.pop();
if(now.x==r&&now.y==s) return now.road;
int temp=mapp[now.x][now.y],cnt=now.road[now.road.size()-1]-'0';
for(int i=1;i<=3;i++){
next=now;
if(p[temp].dir[cnt][i]){
if(cnt==1){
switch(i){
case 1:next.x=now.x-1,next.road=now.road+char('0'+4);break;
case 2:next.y=now.y+1,next.road=now.road+char('0'+1);break;
case 3:next.x=now.x+1,next.road=now.road+char('0'+3);break;
}
}
else if(cnt==2){
switch(i){
case 1:next.x=now.x+1,next.road=now.road+char('0'+3);break;
case 2:next.y=now.y-1,next.road=now.road+char('0'+2);break;
case 3:next.x=now.x-1,next.road=now.road+char('0'+4);break;
}
}
else if(cnt==3){
switch(i){
case 1:next.y=now.y+1,next.road=now.road+char('0'+1);break;
case 2:next.x=now.x+1,next.road=now.road+char('0'+3);break;
case 3:next.y=now.y-1,next.road=now.road+char('0'+2);break;
}
}
else {
switch(i){
case 1:next.y=now.y-1,next.road=now.road+char('0'+2);break;
case 2:next.x=now.x-1,next.road=now.road+char('0'+4);break;
case 3:next.y=now.y+1,next.road=now.road+char('0'+1);break;
}
}
if(judge(next)){
vis[next.x][next.y][next.road[next.road.size()-1]-'0']=1;
qu.push(next);
}
}
}
}
return "-1";
}
int main() {
string name;
while(cin>>name&&name!="END"){
while(!qu.empty()) qu.pop();
memset(mapp,0,sizeof(mapp));
memset(vis,0,sizeof(vis));
guide *p=new guide[100];
node start;
int m,n,r,s;
char sr;
scanf("%d%d%*c%c%*c%d%d",&m,&n,&sr,&r,&s);
if(sr=='N') start.x=m-1,start.y=n,start.road+=char('0'+4);
else if(sr=='S') start.x=m+1,start.y=n,start.road+=char('0'+3);
else if(sr=='E') start.x=m,start.y=n+1,start.road+=char('0'+1);
else start.x=m,start.y=n-1,start.road+=char('0'+2);
vis[start.x][start.y][start.road[start.road.size()-1]-'0']=1;
qu.push(start);
int id=1,x,y;
string ifo;
while(scanf("%d",&x)&&x){
scanf("%d",&y);
mapp[x][y]=id;
while(cin>>ifo&&ifo[0]!='*'){
int len=ifo.size();
int temp=c_to_num(ifo[0]);
for(int i=1;i<len;i++){
p[id].dir[temp][g_to_num(ifo[i])]=true;
}
}
id++;
}
string ans=bfs(r,s,p);
cout<<name<<endl;
if(ans=="-1"){
cout<<" No Solution Possible"<<endl;
continue;
}
cout<<" ("<<m<<','<<n<<')';
int len=ans.size();
for(int i=0;i<len;i++){
if(i%10==9){
cout<<endl;
cout<<" ";
}
if(ans[i]=='1') n+=1;
else if(ans[i]=='2') n-=1;
else if(ans[i]=='3') m+=1;
else m-=1;
cout<<" ("<<m<<','<<n<<')';
}
cout<<endl;
}
return 0;
}