您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  UVa816 uva ACM