UVA ~ 816 ~ Abbott's Revenge (BFS + 打印路径)

题意:有一个最多包含9*9个交叉点的迷宫。输入起点,离开起点时的朝向和终点,求一条最短路(多解时任意输出一个即可)。
这个迷宫的特殊之处在于:进入一个交叉点的方向(用NEWS这四个字母分别表示北东西南,即上右左下)不同,允许出去的方向也不同。例如,1 2 WLF NR ER 表示交叉点(1,2)(上数第1行,左数第2列)有3个路标(字符“”只是结束标志),如果进入该交叉点时的朝向为W(即朝左),则可以左(L)或者直行(F);如果进入时朝向为N或者E则只能右转(R),如图6-14所示。
注意:初始状态是“刚刚离开入口”,所以即使出口和入口重合,最短路也不为空。例如,图6-14中的一条最短路为(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)。

【分析】
本题和普通的迷宫在本质上是一样的,但是由于“朝向”也起到了关键作用,所以需要用到一个三元组(r,c,dir)表示“位于(r,c),面朝dir”这个状态。假设入口位置为(r0,c0),朝向为dir,则初始状态并不是(r0,c0,dir),而是(r1,c1,dir),其中,(r1,c1)是(r0,c0)沿着方向p[r][c][dir]保存了状态(r,c,dir)在BFS树中的父节点。

提示6-22:很多复杂的迷宫问题都可以转化为最短路为题,然后用BFS求解。在套用BFS框架之前,需要先搞清楚图中的“结点”包含哪些内容。
以上内容来自《算法竞赛入门经典》

补充:开一个数组has_edge[r][c][dir1][dri2],表示(r,c)这个点转向方式(dir1,dir2)是否可行。path记录路径,step记录步数(起走过的状态标记作用)。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 15;
const char* dirs = "NESW"; //顺时针旋转
const char* turns = "FLR";
const int Dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};//四个方向
int sr0, sc0, sr, sc, sdir, er, ec, edir;//起点(sr, sc, sdir),终点(er, ec, edir),edir是BFS后得到的
char name[100];//地图名称
bool has_edge[MAXN][MAXN][5][5];//该点能走的方向
struct Node
{
    int r, c, dir; // 站在(r,c),面朝方向dir(0~3分别表示N, E, S, W)
    Node(int r=0, int c=0, int dir=0):r(r), c(c), dir(dir) {}
};
int step[MAXN][MAXN][5];//vis作用和记录步数
Node path[MAXN][MAXN][5];//记录路径
int dir_id(char c) { return strchr(dirs, c) - dirs; }
int turn_id(char c) { return strchr(turns, c) - turns; }
bool read_case()
{
    char temp[100];//开成字符串,跳过两个数字之间的空格
    if (scanf("%s%d%d%s%d%d", name, &sr0, &sc0, temp, &er, &ec) != 6) return false;
    sdir = dir_id(temp[0]);
    sr = sr0 + Dir[sdir][0];
    sc = sc0 + Dir[sdir][1];
    memset(has_edge, 0, sizeof(has_edge));
    int r, c;
    while (~scanf("%d", &r) && r)
    {
        scanf("%d", &c);
        char s[100];
        while (~scanf("%s", s) && s[0] != '*')
        {
            for(int i = 1; i < strlen(s); i++)
                has_edge[r][c][dir_id(s[0])][turn_id(s[i])] = true;
        }
    }
    return true;
}
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 + Dir[dir][0], u.c + Dir[dir][1], dir);
}
bool BFS()
{
    memset(step, -1, sizeof(step));
    path[sr][sc][sdir] = Node(sr0, sc0, sdir);//记录第一步
    step[sr][sc][sdir] = 0;
    queue<Node> Q;
    Q.push(Node(sr, sc, sdir));
    while (!Q.empty())
    {
        Node u = Q.front(); Q.pop();
        if (u.c == ec && u.r == er) { edir = u.dir; return true; }
        for (int i = 0; i < 3; i++)//3种转向方式
        {
            Node v = walk(u, i);
            if (v.r >= 1 && v.c >= 1 && v.r <= 9 && v.c <= 9
            && has_edge[u.r][u.c][u.dir][i] && step[v.r][v.c][v.dir] < 0)
            {
                step[v.r][v.c][v.dir] = step[u.r][u.c][u.dir] + 1;
                path[v.r][v.c][v.dir] = u;
                Q.push(v);
            }
        }
    }
    return false;
}
void print_ans()
{
    vector<Node> ans;
    Node u(er, ec, edir);
    while (1)
    {
        ans.push_back(u);
        if (u.r == sr0 && u.c == sc0) break;
        u = path[u.r][u.c][u.dir];
    }
    int cnt = 0;
    for (int i = ans.size()-1; i >= 0; i--)
    {
        if (cnt % 10 == 0) printf(" ");
        printf(" (%d,%d)", ans[i].r, ans[i].c);
        if(++cnt % 10 == 0 || i == 0) printf("\n");
    }
}
int main()
{
    //freopen("C:\\Users\\张松超\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\张松超\\Desktop\\out.txt", "w", stdout);
    while (read_case())
    {
        printf("%s\n", name);
        if (BFS()) print_ans();
        else printf("  No Solution Possible\n");
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值