https://vjudge.net/problem/UVA-816
分析输入:
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
WLF的意思是,有个告示牌,从West西方向来的人能看到,他们可以选择向L和F走,也就是左转或者直行。
然后输入中用这样的字符串,对每个点加了限制条件,即从特定方向来的人只能选择规定的走法:直行、左转、右转中的若干选项。
题目要求在上述限制下,找出一条入口到出口的最短路。
下面是紫薯代码以及详解:
const char* dirs = "NESW"; //顺时针旋转,北东南西
const char* turns = "FLR";
//数字对方向编号,下面两个函数将字符方向转换为方向编号
int dir_id(char c) {return strchr(dirs,c) - dirs;}
int turn_id(char c) {return strchr(turns,c) - turns;}
//四个方向
const int dr[] = {-1,0,1,0};
const int dc[] = {0,1,0,-1};
//结点,三元组(r,c,dir) 表示位于(r,c),面朝dir
struct Node{
int r;
int c;
int dir;
Node():r(0),c(0),dir(0){}
Node(int a1,int a2,int a3):r(a1),c(a2),dir(a3){}
};
//d表示(r,c,dir)到初始状态的最短路长度
//p表示(r,c,dir)在BFS树中的父结点
//has_edge[r][c][dir][turn]表示当前(r,c,dir)能否沿着turn方向转弯
int d[10][10][4];
Node p[10][10][4];
int has_edge[10][10][4][3];
//对应入口、初始结点、目标结点
int r0,c0,r1,c1,r2,c2,dir,row,col;
//打印答案函数的声明
void print_ans(Node u);
//行走函数,不负责可走性判断,只管走,inside()函数用来判断出界
Node walk(const Node& u,int turn){
int dir = u.dir;
if(turn == 1) dir = (dir+3) % 4;//逆时针
if(turn == 2) dir = (dir+1) % 4;//顺时针
return Node(u.r + dr[dir],u.c+dc[dir],dir);//dir不改就是直行
}
//判断出界
bool inside(int r,int c){
if(r<=0||r>row) return false;
if(c<=0||c>col) return false;
return true;
}
//bfs
void solve(){
queue<Node> q;
memset(d,-1,sizeof(d));
Node u(r1,c1,dir);
d[u.r][u.c][u.dir] = 0;//(r,c,dir)是初始结点,注意初始结点不是入口entrance,而是从入口走了一步后的结点
q.push(u);//初始结点入队,开始BFS
while(!q.empty()){
Node u = q.front();q.pop();
if(u.r == r2 && u.c == c2) {print_ans(u); return;}//边界,如果到了终点
for(int i = 0;i<3;i++){//向三个方向走
Node v = walk(u,i);//v是从u向i方向走后的点,不管可不可达
//如果允许向i方向走,而且v点在迷宫内,而且v点没有访问过的话,就访问v
if(has_edge[u.r][u.c][u.dir][i] && inside(v.r,v.c) && d[v.r][v.c][v.dir] < 0){
d[v.r][v.c][v.dir] = d[u.r][u.c][u.dir] + 1;//将到v结点的路径长度更新
p[v.r][v.c][v.dir] = u;//更新v的父结点为u
q.push(v);//入队作为u的下一层结点的v
}
}
}
printf(" No Solution Possible\n");//如果BFS走完了,但是没有到达终点,返回无解
}
//参数是目标结点
void print_ans(Node u){
//从目标结点逆序追溯到初始结点
vector<Node> nodes;
for(;;){
nodes.push_back(u);
if(d[u.r][u.c][u.dir] == 0) break;//初始结点
u = p[u.r][u.c][u.dir];
}
nodes.push_back(Node(r0,c0,dir));//最后把入口结点加入
//打印解。每行10个
int cnt = 0;
for(int i = nodes.size()-1;i>=0;i--){
if(cnt % 10 == 0) printf(" ");
printf(" (%d,%d)",nodes[i].r,nodes[i].c);
if(++cnt % 10 == 0) printf("\n");
}
if(nodes.size() %10 !=0) printf("\n");
}
int main(){
char name[20],direction;
while(scanf("%s",name)!=EOF){
memset(has_edge,0,sizeof(has_edge));
memset(p,0,sizeof(p));
if(!strcmp(name,"END")) break;
scanf("%d %d %c %d %d",&r0,&c0,&direction,&r2,&c2);
if(r0>row) row = r0;
if(r2>row) row = r2;
if(c0>col) col = c0;
if(c2>col) col = c2;
dir = dir_id(direction);
r1 = r0+dr[dir];
c1 = c0+dc[dir];
int rr,cc;
while(scanf("%d",&rr)!=EOF&&rr){
scanf("%d",&cc);
if(rr>row) row = rr;
if(cc>col) col = cc;
char temp[5];
while(scanf("%s",temp)!=EOF){
if(temp[0]=='*') break;
int dirr = dir_id(temp[0]);
for(int i=1;i<strlen(temp);i++){
has_edge[rr][cc][dirr][turn_id(temp[i])] = 1;
}
}
}
printf("%s\n",name);
solve();
}
return 0;
}