Abbott的复仇(UVa816 紫书p165)

题目描述

输入格式

输出格式

题意翻译

给出一个箭头迷宫,在每个路口处,如果你从某个方向进入了该路口,那么路口的地面上在靠近你的方向会画有一组箭头,它们相对于你的方向可以是向左,向前,向右,或者是它们的任意组合。

当你从某一方向进入某个入口时,下一步只能在这个入口对应方向上标记的箭头中选一个方向继续行进。在起点时,可以选择任何方向。

给出起点和终点,求它们之间的最短路径。

每条边的长度为1

输入输出样例

输入 

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
NOSOLUTION
3 1 N 3 2
1 1 WL NR *
1 2 NL ER *
2 1 SL WR NFR *
2 2 SR EL *
0
END
Figure 1: An Example Walk-Through
Arrow Maz

输出 

SAMPLE
(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)
NOSOLUTION
No Solution Possible

代码

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>

using namespace std;

const int N = 10;

int d[N][N][4];

bool has_edge[N][N][4][4];//当前状态是(r, c, dir) 是否可以沿着转弯方向turn 行走 

const char *dirs = "NESW";//方向 
const char *turns = "FLR";//转向

int r1, c1, dir, r2, c2, r0, c0;

//将4个方向和3种转弯方式编号为0~3和0~2 
int dir_id(char c){return strchr(dirs, c) - dirs;}
int turn_id(char c){return strchr(turns, c) - turns;} 

struct Node
{
	int r, c, dir;
	Node(int r, int c, int dir):r(r), c(c), dir(dir){}
	Node(){}
};

Node p[N][N][4];//距离 父节点

int dr[] = {-1, 0, 1, 0};
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.r + dr[dir], u.c + dc[dir], dir); 
}

bool inside(int r, int c)
{
	return r >= 1 && r <= 9 && c >= 1 && c <= 9;
}

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) cout << ' ';
		cout << ' ' << '(' << nodes[i].r << ',' << nodes[i].c << ')'; 
		if(++ cnt % 10 == 0) cout << endl;
	}
	if(nodes.size() % 10 != 0) cout << endl;
} 

void solve()
{
	queue<Node> q;
	memset(d, -1, sizeof d);
	Node u(r1, c1, dir);
	d[r1][c1][dir] = 0;
	q.push(u);
	
	while(!q.empty())
	{
		auto u = q.front(); q.pop();
		if(u.r == r2 && u.c == c2){print_ans(u); return ;}
		for(int i = 0; i < 3; i ++)
		{
			auto v = walk(u, i);
			//能走到 + 在里面 + 没被遍历过 
			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;
				p[v.r][v.c][v.dir] = u;
				q.push(v); 
			}
		}
	}
	//顶头两个空格 
	cout << "  No Solution Possible" << endl;
}

string maze;

bool input()
{

	cin >> maze;
	if(maze == "END") return false; 
	memset(has_edge, 0, sizeof has_edge);
	char op;
	cin >> r0 >> c0 >> op >> r2 >> c2;
	dir = dir_id(op);
	r1 = r0 + dr[dir];
	c1 = c0 + dc[dir];
	
	//读入各个边
	int r, c;
	string tmp;
	while(cin >> r, r)
	{
		cin >> c;
		while(cin >> tmp, tmp != "*")
		{
			for(int i = 1; i < tmp.size(); i ++)
				has_edge[r][c][dir_id(tmp[0])][turn_id(tmp[i])] = true;
		}
	}
	return true;
}

int main()
{
	while(input())
	{
		cout << maze << endl;
		solve();
	}
		
	return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值