BFS+优先队列

湘潭大学2018
小明来到一个由n x m个格子组成的迷宫,有些格子是陷阱,用’#‘表示,小明进入陷阱就会死亡,’.'表示没有陷阱。小明所在的位置用’S’表示,目的地用’T’表示。

小明只能向上下左右相邻的格子移动,每移动一次花费1秒。

有q个单向传送阵,每个传送阵各有一个入口和一个出口,入口和出口都在迷宫的格子里,当走到或被传送到一个有传送阵入口的格子时,小明可以选择是否开启传送阵。如果开启传送阵,小明就会被传送到出口对应的格子里,这个过程会花费3秒;如果不开启传送阵,将不会发生任何事情,小明可以继续向上下左右四个方向移动。

一个格子可能既有多个入口,又有多个出口,小明可以选择任意一个入口开启传送阵。使用传送阵是非常危险的,因为有的传送阵的出口在陷阱里,如果小明使用这样的传送阵,那他就会死亡。也有一些传送阵的入口在陷阱里,这样的传送阵是没有用的,因为小明不能活着进入。请告诉小明活着到达目的地的最短时间。

输入描述:
有多组数据。对于每组数据:
第一行有三个整数n,m,q(2≤ n,m≤300,0≤ q ≤ 1000)。
接下来是一个n行m列的矩阵,表示迷宫。
最后q行,每行四个整数x1,y1,x2,y2(0≤ x1,x2< n,0≤ y1,y2< m),表示一个传送阵的入口在x1行y1列,出口在x2行y2列。

输出描述:
如果小明能够活着到达目的地,则输出最短时间,否则输出-1。
示例1
输入
5 5 1
…S…

.###.

…T…
1 2 3 3
5 5 1
…S…

.###.

…T…
3 3 1 2
5 5 1
S.#…
…#…
###…

…T
0 1 0 2
4 4 2
S#.T
.#.#
.#.#
.#.#
0 0 0 3
2 0 2 2
输出
6
8
-1
3

BFS简单题
因花费秒数不全为1(即相当于点之间距离不全为1),所以要用优先队列,尽可能让秒数少的在队列前面,这样在前面的一旦到达终点所用时长必然是最短时长。
优先队列的使用

struct node
{
	int x;
	int y;
	int step;
	bool operator < (const node& rh)const{
		return step > rh.step;
	}
}Node;

把时长从小到大排队,相当于多了一个关键字,时长越小的越靠前。
此题需要注意传送走到的点不能立马标记,因为有可能传过去花费时间比走过去花费时间长,所以所有的标记应该在上下左右四个方向走的时候标记而不是传送后标记。
此题还可建图最短路,维护边权为1和3。后续补上此方法。
代码如下

#include <iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int r,n,m;
int sx,sy,tx,ty;
int vis[305][305];
char mp[305][305];
struct node
{
	int x;
	int y;
	int step;
	bool operator < (const node& rh)const{
		return step > rh.step;
	}
}Node;
priority_queue<node> q;
node send[305][305];
void bfs()
{
	while(!q.empty()) q.pop();

	vis[sx][sy] = 1;
	Node.x=sx,Node.y=sy,Node.step=0;
	q.push(Node);
	while(!q.empty())
	{
		node tmp = q.top();
		int x = tmp.x;
		int y = tmp.y;
		int step = tmp.step;

		if(x==tx && y==ty)
		{
			printf("%d\n",step);
			return;
		}	
		q.pop();
		//有传送门
		if(send[x][y].step==1 && vis[send[x][y].x][send[x][y].y] != 1)
		{
			node sendstruct;
			sendstruct.x = send[x][y].x;
			sendstruct.y = send[x][y].y;
			sendstruct.step = step+3;
			q.push(sendstruct);
			//不能标记传送过去的点,有可能直接走比传送还近
		}
		for (int i=0;i<4;i++) 
		{
			node d;
			d.x = x+dir[i][0];
			d.y = y+dir[i][1];
			d.step = step+1;
			if(d.x<0 || d.y<0 || d.x>n || d.y>m || vis[d.x][d.y]==1 || mp[d.x][d.y]=='#') 
				continue;
			q.push(d);
			vis[d.x][d.y]=1;
		}
	}
	printf("-1\n");
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&r))
	{
		memset(vis,0,sizeof(vis));
		memset(send,0,sizeof(send));
		memset(mp,'\0',sizeof(mp));
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				scanf(" %c",&mp[i][j]);
				if(mp[i][j]=='S')
					sx=i,sy=j;
				else if(mp[i][j]=='T')
					tx=i,ty=j;
			}
		}

		int x1,x2,y1,y2;
		for(int i=0;i<r;i++)
		{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			send[x1][y1].x = x2;
			send[x1][y1].y = y2;
			if(mp[x2][y2] != '#')
				send[x1][y1].step = 1;
			//step等于一表示可行,等于0表示直接传到陷阱
		}
		bfs();
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值