C4练习-解救小Q


解救小Q

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

小Q被邪恶的大魔王困在了迷宫里,love8909决定去解救她。迷宫里面有一些陷阱,一旦走到陷阱里,就会被困身亡:(,迷宫里还有一些古老的传送阵,一旦走到传送阵上,会强制被传送到传送阵的另一头。

现在请你帮助love8909算一算,他至少需要走多少步才能解救到小Q?

Input

第一行为一个整数 T

,表示测试数据组数。

每组测试数据第一行为两个整数 N

M ,( 1N,M50

)表示迷宫的长和宽。

接下来有 N

行,每行 M

个字符,是迷宫的具体描述。

  • .表示安全的位置
  • #表示陷阱,
  • Q表示小Q的位置
  • L表示love8909所在位置,

数据保证love8909只有一个,数据也保证小Q只有一个。

小写字母a-z表示分别表示不同的传送阵,数据保证传送阵两两配对。

Output

每组数据输出一行,解救小Q所需的最少步数,如果无论如何都无法救小Q,输出-1

Sample input and output

Sample InputSample Output
2

5 5
....L
.###.
b#b#a
##.##
...Qa

5 5
....L
.###.
.#.#.
##.##
...Q.
3
-1

http://acm.uestc.edu.cn/#/problem/show/149

这道题就是一道bfs的搜索题,但是引入了传送门这种东西,我的办法是标记传送门的第一个节点,当然也可以标记传送过去的那个节点,但是绝对不能一次把两个节点都标记了,因为这个传送门是双向的,他是有可能要回来的,例如

..#.a

.#Q#.

.#.#.

..aL.

这种图的走法就是先进a,到达上面那个a,然后向左走一步,再向右走一步回到下面那个a,再向上走,走到Q,一共是五步。至于怎么标记这些传送门的地址,就各有各的办法了,我是用读入这个图的时候用map标记这种传送门,和find一起用。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=55;
bool vst[maxn][maxn]; // 访问标记
int dir[4][2]={0,1,0,-1,1,0,-1,0}; // 方向向量
char G[maxn][maxn];
int n,m;

struct State // BFS 队列中的状态数据结构
{
	int x,y; // 坐标位置
	int Step_Counter; // 搜索步数统计器
}Q,L,tmp,tran[maxn][maxn];

bool CheckState(State s) // 约束条件检验
{
	if(!vst[s.x][s.y] && G[s.x][s.y]!='#' && s.x>=0 && s.x <n && s.y>=0 && s.y <m) // 满足条件		
		return 1;
	else // 约束条件冲突
		return 0;
}

void bfs(State st)
{
	queue <State> q; // BFS 队列
	State now,next; // 定义2 个状态,当前和下一个
	st.Step_Counter=0; // 计数器清零
	q.push(st); // 入队	
	vst[st.x][st.y]=1; // 访问标记
	while(!q.empty())
	{
		now=q.front(); // 取队首元素进行扩展
		if(now.x==L.x&&now.y==L.y) // 出现目标态,此时为Step_Counter 的最小值,可以退出即可
		{
			printf("%d\n",now.Step_Counter); // 做相关处理
			return;
		}
	for(int i=0;i<4;i++)
	{
		next.x=now.x+dir[i][0]; // 按照规则生成	下一个状态
		next.y=now.y+dir[i][1];
		next.Step_Counter=now.Step_Counter+1; // 计数器加1
		if(CheckState(next)) // 如果状态满足约束条件则入队
		{
			vst[next.x][next.y]=1; //访问标记
			if(G[next.x][next.y]>=97&&G[next.x][next.y]<=122)
			{
				next.x=tran[now.x+dir[i][0]][now.y+dir[i][1]].x;
				next.y=tran[now.x+dir[i][0]][now.y+dir[i][1]].y;
			}
			q.push(next);			
		}
	}
	q.pop(); // 队首元素出队
	}
 return;
}

int main()
{
	int t,i,j;	
	map<char ,State >::iterator it;
	scanf("%d",&t);
	while(t--)
	{
		map<char ,State>MAP;
		memset(vst,0,sizeof(vst));
		scanf("%d%d",&n,&m);
		for(i=0;i<n;i++)
		{
			scanf("%s",G[i]);
			for(j=0;j<m;j++)
				if(G[i][j]>=97&&G[i][j]<=122)
				{
					it=MAP.find(G[i][j]);
					if(it==MAP.end())
					{
						tmp.x=i;
						tmp.y=j;
						MAP[G[i][j]]=tmp;
					}						
					else
					{
						tmp=it->second;
						tran[tmp.x][tmp.y].x=i;
						tran[tmp.x][tmp.y].y=j;
						tran[i][j].x=tmp.x;
						tran[i][j].y=tmp.y;
					}						
				}
				else if(G[i][j]=='Q')
				{
					Q.x=i;
					Q.y=j;
				}
				else if(G[i][j]=='L')
				{
					L.x=i;
					L.y=j;
				}
		}
		bfs(Q);
		if(!vst[L.x][L.y])
			printf("-1\n");
	}
 return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值