UVa 816 Abbott's Revenge【BFS】【迷宫】

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值