816 - Abbott's Revenge
2016-05-16 20:45
337 查看
Abbott’s Revenge
PS:因为该题排版较麻烦,这里给出OJ网址:UVa816 - Abbott’s Revenge有一个最多包含9*9个交叉点的迷宫。输入起点、离开起点时的朝向和终点,求一条最短路(多解时任意输出一个即可)。
这个迷宫的特殊之处在于:进入一个交叉点的方向(用NEWS这4个字母分别表示北东西南,即上右左下)不同,允许出去的方向也不同。例如,1 2 WLF NR ER 表示交叉点(1,2)(上数第1行,左数第2列)有3个路标(字符“”只是结束标志),如果进入该交叉点时的朝向为W(即朝左),则可以左转(L)或者直行(F);如果进入时朝向为N或者E则只能右转(R),如图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)。
#include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; // 节点类 // 站在(row,column)上,朝向dir方 // dir的取值范围为[0-3],分别表示N,E,S,W struct Node { // 行 int row; // 列 int column; // 朝向 int dir; // 构造方法 Node(int row = 0, int column = 0, int dir = 0):row(row),column(column),dir(dir){ } }; const int maxNum = 10; // 站在(i,j)面朝k方位,能否转向到m // m取值范围为[0,2],0:向前,1:向左,2:向右 int has_edge[maxNum][maxNum][4][3]; // 初始点到(r,c,dir)的最短路径长度 int d[maxNum][maxNum][4]; // (r,c,dir)父节点的三元组 Node p[maxNum][maxNum][4]; // 初始点(r0,c0)及其朝向 int r0, c0, dir; // 初始点向dir方向走了一步的位置(r1,c1) int r1, c1; // 出口位置(r2,c2) int r2, c2; // 朝向,上右下左 const char* dirs = "NESW"; // 转向,前左右 const char* turns = "FLR"; // 通过字符c来判断朝向 // 0:N 1:E 2:S 3:W // 原理为strchr函数是在dirs中找c字符 // 找到,则返回指向c字符位置的指针 // 然后减去dirs的指针,就能获取相应位置了 int dir_id(char c) { return strchr(dirs, c) - dirs; } // 通过字符c来判断转向 // 0:F 1:L 2:R // 原理和dir_id()一样 int turn_id(char c) { return strchr(turns, c) - turns; } // 位移变化数组 // 0:组合起来向上了一步 // 1:组合起来向右了一步 // 2:组合起来向下了一步 // 3:组合起来向左了一步 const int dr[] = {-1, 0, 1, 0}; const int dc[] = { 0, 1, 0, -1}; // 行走函数,通过该函数可以获得下一步的位置 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.row + dr[dir], u.column + dc[dir], dir); } // 读取一组用例 bool readCase() { // 用例名和初始朝向 char s[99], s2[99]; if(scanf("%s%d%d%s%d%d", s, &r0, &c0, s2, &r2, &c2) != 6) { return false; } printf("%s\n", s); // 初始化初始节点的下一个节点 dir = dir_id(s2[0]); r1 = r0 + dr[dir]; c1 = c0 + dc[dir]; memset(has_edge, 0, sizeof(has_edge)); while(true) { // 行列 int r, c; scanf("%d", &r); if(r == 0) { break; } scanf("%d", &c); while(scanf("%s", s) == 1 && s[0] != '*') { // s[0]为当前朝向 // s[1-n]为能够转向的方向 for(int i = 1; i < strlen(s); i++) { has_edge[r][c][dir_id(s[0])][turn_id(s[i])] = 1; } } } return true; } // 打印信息 void printAns(Node u) { // 从目标节点追溯到初始节点 vector<Node> nodes; while(true) { nodes.push_back(u); // 初始节点的下一个节点退出 if(d[u.row][u.column][u.dir] == 0) { break; } u = p[u.row][u.column][u.dir]; } // 初始节点 nodes.push_back(Node(r0, c0, dir)); // 打印结果,每行10个 // 每行最前面两个空格,(r,c)间一个空格 int num = 0; for(int i = nodes.size() - 1; i >= 0; i--) { if(num % 10 == 0) { printf(" "); } printf(" (%d,%d)", nodes[i].row, nodes[i].column); if(++num % 10 == 0) { printf("\n"); } } if(nodes.size() % 10 != 0) { printf("\n"); } } // 是否在范围内 bool inside(int r, int c) { return r >= 1 && r <= 9 && c >= 1 && c <= 9; } // 采用BFS解决问题 void solve() { // 节点队列 queue<Node> q; memset(d, -1, sizeof(d)); Node u(r1, c1, dir); d[u.row][u.column][u.dir] = 0; q.push(u); while(!q.empty()) { Node u = q.front(); q.pop(); // 找到出口 if(u.row == r2 && u.column == c2) { printAns(u); return; } // 0:F 1:L 2:R for(int i = 0; i < 3; i++) { // 转向方位都试探性的走一步 Node v = walk(u, i); // 满足能够向该方向转 // 移动之后还在边界内 // 该点还没有被走过 if(has_edge[u.row][u.column][u.dir][i] && inside(v.row, v.column) && d[v.row][v.column][v.dir] < 0) { // 距离+1 d[v.row][v.column][v.dir] = d[u.row][u.column][u.dir] + 1; // 设置父节点 p[v.row][v.column][v.dir] = u; // 入队列 q.push(v); } } } printf(" No Solution Possible\n"); } int main () { while(readCase()) { solve(); } return 0; }
相关文章推荐
- 简单的四则运算
- 数的奇偶性
- ACMer博客瀑布流分析
- ACM程序设计大赛题目分类
- 计算字符串最后一个单词长度
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 1.10055 - Hashmat the brave warrior
- 2.10071 - Back to High School Physics
- 3.458 - The Decoder
- 4.694 - The Collatz Sequence
- 6.494 - Kindergarten Counting Game
- 7.490 - Rotating Sentences
- 8.414 - Machined Surfaces
- 9.488 - Triangle Wave
- A.457 - Linear Cellular Automata
- B.489 - Hangman Judge