UVA Abbott's Revenge

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;
struct node
{
	int r, c, dir;//横行,竖行及方向
	node(int r=0,int c=0,int dir=0):r(r),c(c),dir(dir){}//结构体中定义函数
};
const int maxn = 10;
int has_edge[maxn][maxn][4][3];//判断方向及转向是否可行,用于判断行走的数组
int d[maxn][maxn][4];//记录行走的路径
node p[maxn][maxn][4];//用于记录下个行走的节点
int r0, c0, r1, c1, dir1,r2,c2;
const char* dirs = "NESW";
const char* turns = "FLR";
const int dr[] = { -1,0,1,0 };
const int dc[] = { 0,1,0,-1 };//注意四个方向的转向,利用数组控制
int dirid(char c)//该函数作用为将字符在字符串中的数字转换为对应数字,直接利用地址相减
{
	return strchr(dirs, c) - dirs;
}
int turnid(char c)//将几种方向转换为相应数字
{
	return strchr(turns, c) - turns;
}
bool inside(int r, int c)//判断出界,进行相应转向后,可能造成出界的情况
{
	return (r >= 1 && r <= 9 && c >= 1 && c <= 9);
}
node change(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 read()//读入函数
{
	char s1[99], s2[99];
	scanf("%s", s1);
	if (strcmp(s1, "END") == 0)
		return false;
	scanf("%d%d%s%d%d", &r0, &c0, s2, &r2, &c2);//读入起点与终点,注意起始点问题,相应状态为刚好离开起点
	printf("%s\n", s1);
	dir1 = dirid(s2[0]);
	r1 = r0 + dr[dir1];
	c1 = c0 + dc[dir1];//转化起点,起点为离开初始点之后的状态
	memset(has_edge, 0, sizeof(has_edge));//has_edge数组用于判断相应转弯方案是否可行
	while (1)//读入数据,处理最终可行的方向
	{
		int r, c;
		char s[99];
		scanf("%d", &r);
		if (r == 0)
			break;
		scanf("%d", &c);
		while (scanf("%s", s) == 1 && s[0] != '*')//方便转换方向与转向,分别对字符串进行处理
		{
			for (int i = 1; i < strlen(s); i++)//注意方向与转向输入方式
			{
				has_edge[r][c][dirid(s[0])][turnid(s[i])] = 1;//记录相应转换方向可行,方便记录路径
			}
		}
	}
	return true;
}
void print_f(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];//p保留父节点,利用p数组进行回溯
	}//最后输出时直接进行倒序输出
	nodes.push_back(node(r0, c0, dir1));
	int count = 0;//count用于格式控制,注意相应格式控制的细节
	for (int i = nodes.size() - 1; i >= 0; i--)
	{
		if (count % 10 == 0)//10个一组,注意起始有空格
			printf(" ");
		printf(" (%d,%d)", nodes[i].r, nodes[i].c);
		if (++count % 10 == 0)
			printf("\n");
	}
	if (nodes.size() % 10 != 0)//不是所有的行的情况都是10的整数倍,细节格式
		printf("\n");
}
void solve()//利用bfs不断递归寻找可能出口
{
	queue<node>q;//利用队列不断寻找出口
	memset(d, -1, sizeof(d));//注意初始化起始点,控制条件的结束
	node u(r1, c1, dir1);//以起始点进行深度搜索
	d[u.r][u.c][u.dir] = 0;
	q.push(u);
	while (!q.empty())
	{
		node u = q.front(); q.pop();
		if (u.r == r2 && u.c == c2)//找到终点,输出结果,返回,注意寻找结束点的控制
		{
			print_f(u);
			return;
		}
		for (int i = 0; i < 3; i++)//用循环遍历可能的方向及转向,i控制旋转的方向
		{
			node v = change(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记录可能的行走路径
			{
				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);//相应的节点可行,放入队列进行搜索,队列中存在先后处理的关系,满足深度搜索
			}
		}
	}
	printf("  No Solution Possible\n");//没找到最后的结果,走不通
}
int main()
{
	while (read())
	{
		solve();
	}
	getchar();
	getchar();
	return 0;
}

*1、有关结构体中构造函数, node(int r=0,int c=0,int dir=0):r(r),c(c),dir(dir){}
注意构造函数时相应的格式,当括号内已经声明变量时,后面则写入变量名,注意区别无参构造函数的定义方式,掌握结构体中定义函数的方法

*2、本题巧妙之处在于利用strchr函数巧妙地将字符转换为数字,注意地址相减的方法,得出地址之间的差值,同时利用循环遍历枚举判断可能出现的转向方式
3、注意细节处理,逆时针,顺时针的处理方式

4、理解bfs使用队列的本质,队列有先后的处理关系,注意理解其中的顺序关系,每个分支都进行搜索
5、注意本题输出格式问题,导致我之前一直WA,注意空格为2个!!!

*6、注意题中父节点与子节点关系的建立,利用p数组存储父节点,方便回溯时使用,同时输入时注意细节的处理,起始状态为刚刚离开初始点之时
7、bfs以及dfs问题都应注意控制边界的问题,避免搜索时超出界限

8、注意has_edge数组的检索,应该是判断未转向之前是否存在相应的状态,易错点,注意理解has_edge数组的作用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值