目录
Description
龙神觉得无聊于是来到了地下城,这里是一个巨大的迷宫,有一些可以通行的路、一些不可以通行的墙,还有一些怪物。虽然龙神可以轻易地干掉这些怪物,但他觉得这样就太没意思了,他观察到这些怪物每 k 秒会消失一次(例如 k 等於 3 时,则第 3 逗號 空格 6 逗號 空格 9 逗號 空格 中线省略号 秒怪物是消失的),每一秒龙神可以选择向上下左右行走一步(不能在原地不动)。龙神想知道在避开全部怪物的条件下,到达出口所需要的最短时间。
Input 第一行输入一个整数 T 空格 左括号 1 小於等於 T 小於等於 10 右括号,代表用例组数。 每组用例的第一行包括三个整数 n 逗號 空格 m 空格 左括号 1 小於等於 n 逗號 空格 m 小於等於 100 右括号 和 ,k 空格 左括号 1 小於等於 k 小於等於 50 右括号 分别表示地下城迷宫的行数、列数、怪物的消失间隔。 接下来的 n 行代表迷宫,.表示可以通行的路,#表示墙,*表示怪物,S表示起点,E代表出口。
Output 输出一个整数,表示龙神走出地下城迷宫的最短时间,如果龙神不能走出迷宫则输出-1。
Source BITACM2018第一轮积分赛(三)- Problem J
思路
这道题,仍然是bfs。
但是和上一道不一样了。
你可以上csdn搜bfs迷宫例题,最基本的都是走迷宫:一张图,一些墙,一个起点,一个终点,最短路径。这种题的简单之处在于这个迷宫本身已经是图了,所以只需要bfs即可
这种迷宫目前我见过三种变式:
- 21题这种,没有给你图,需要自己用map<int,vector<int>>去构造
- 22题这种,给了现成的图,但是有怪,而且也不是最短路径,而是最短时间
- 还有一种,要你输出最短路径
这题大体思路不难,就是走迷宫嘛,不计时间,套csdn的模板即可。
关键就是理解一下为什么要开三维的vis数组,假设我的第三维一直更新,那么怎么重复都不会触发已经访问标记,直到我走过一个k周期,就有可能触发了。
其实理论上不考虑时间的话是不用开vis的,因为在怪的作用下,我们是可以回头的(这个时候已经不是最短步数的迷宫问题了)
之所以要开vis,是为了减少无谓的回头,但是又出来一个新的问题:第三维开多大?
为了解决上限不明的问题,就使用了%来限制范围,节省空间
%的合理性:在时间time和时间time+k访问同一个位置是完全等价的!
然而time和time+k这两个时间点的怪是完全一样的,
也就是说,在周期上,反而没有怪的影响了,也就是说在这一步走就是彻底没有意义的了,故跳过
代码
原题,J
我的版本
#include<cstdio>
#include<cstring>
#include<queue>
#define SIZE 101
const int dir[2][4] = { {1,0,0,-1},
{0,1,-1,0} };
char maze[SIZE][SIZE];
bool vis[SIZE][SIZE][51];
struct point {
int x;
int y;
int time;
//point(int tx, int ty, int ttime)//定义一个构造函数快速录入
//{
// x = tx;
// y = ty;
// time = ttime;
//}
};
int main(void)
{
freopen("input.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--)
{
//对每个地图进行操作
//初始化
point start, now, next, end;
int n, m, k, ch;
int ans = -1;
std::queue <point> q;
memset(vis, false, sizeof(vis));
//读入图
scanf("%d %d %d\n", &n, &m, &k);//注意吸收换行
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
ch = getchar();
maze[i][j] = ch;
if (ch == 'S')//记录起点
{
start.x = i;
start.y = j;
start.time = 0;
}
if (ch == 'E')
{
end.x = i;
end.y = j;
}
}
getchar();//每一行都有换行符
}
//bfs
q.push(start);
vis[start.x][start.y][start.time % k] = true;
while (!q.empty())
{
//取出
now = q.front();
q.pop();
if (now.x == end.x && now.y == end.y)
{
ans = now.time;
break;
}
//放入下一步
for (int i = 0; i < 4; i++)
{
next.x = now.x + dir[0][i];
next.y = now.y + dir[1][i];
next.time = now.time + 1;
//注意判断边界,墙,重复访问,怪
if (next.x<1 || next.x>n || next.y > m || next.y < 1
|| maze[next.x][next.y] == '#'
|| vis[next.x][next.y][next.time % k]
|| (maze[next.x][next.y] == '*' && next.time % k != 0))//这块为了可读性写了!=0,其实不写也可以
{
continue;
}
q.push(next);
vis[next.x][next.y][next.time % k] = true;
}
}
printf("%d\n", ans);
}
return 0;
}