目录
题目
Description
龙龙最近迷上了一款名叫 PUBG(PLAYERUNKNOWN’S BATTLEGROUNDS)的手游,那是一款关乎生存挑战的 RPG 逃亡游戏。
考虑到游戏的环节过于复杂,龙龙决定简化一下场景:整个地图可以看做一个长为 宽为 的二维格点平面。龙龙需要从 逃亡到 以逃离毒圈,但有些格点上存在障碍#不能行走,有些格点是沙地.。龙龙只能移动在允许行走的沙地上,同时每一时刻,龙龙只能朝着当前位置周围的上、下、左、右四个方向移动。同时因为龙龙使用了能量饮料,每分钟最多可以朝着一个方向行走 步。
毒圈快要来啦,请你帮龙龙尽快安排一下可行的路线,使得它能够以最短的时间顺利进圈。
Input
第一行输入三个正整数 和 表示地图的大小还有龙龙每分钟最多可以移动的步数;
接下来 行,每行包含 个字符,其中第 行第 个字符表示坐标 的路况,它可能是#,这表示这个格点是障碍区,不能行走,也可能是.表示沙地;
最后一行输入四个正整数 由空格间隔开,表示龙龙的初始位置和目标位置。
Output
请输出一个正整数 ,表示龙龙从 到 进圈最少需要的时间(分钟),如果龙龙最终不能进圈,则请输出-1。
Hint
对于样例,龙龙第一分钟走 步,从 到 ,第二分钟从 到 ,第三分钟从 走到 顺利进圈。
思路
首先,程序读取输入的地图大小n和m,以及龙龙每分钟最多可以移动的步数k。然后,程序读取地图的每一行,并将其存储在map数组中。
int n, m, k; // 地图大小n和m,每分钟最多移动步数k
int x1, y1, x2, y2; // 初始位置和目标位置的坐标
scanf("%d%d%d", &n, &m, &k); // 读取输入
for (int i = 0; i < n; ++i) {
scanf("%s", &map[i]); // 读取地图的每一行
}
接下来,程序读取龙龙的初始位置和目标位置。将初始位置加入到队列que中,并初始化ans为-1(表示无法进圈)。
scanf("%d%d%d%d", &x1, &y1, &x2, &y2); // 读取初始位置和目标位置
que.push(node(x1 - 1, y1 - 1, 0)); // 将初始位置加入队列
int ans = -1; // 初始化答案为-1,表示无法进圈
然后,程序开始进行BFS搜索。每次从队列中取出一个节点tmp,然后根据龙龙可以移动的步数k,尝试向上、下、左、右四个方向移动。如果移动后的位置越界或是障碍物(#),则停止继续移动。如果移动到了目标位置,则更新ans为当前步数加1,并输出ans,然后程序结束。否则,将新的位置加入队列que中,并将visited数组中对应的位置标记为已访问。
que.push(node(x1 - 1, y1 - 1, 0)); // 将初始位置加入队列
int ans = -1; // 初始化答案为-1,表示无法进圈
while (!que.empty()) {
node tmp = que.front(); // 取出队列中的第一个节点
que.pop(); // 弹出队列中的第一个节点
for (int i = 0; i < 4; ++i) { // 尝试四个方向移动
for (int j = 1; j < k + 1; j++) { // 尝试每个方向移动k步
int x = tmp.x + dir_x[i] * j; // 计算新的x坐标
int y = tmp.y + dir_y[i] * j; // 计算新的y坐标
// 如果移动后的位置越界或是障碍物,则停止继续移动
if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == '#') {
break;
}
// 如果位置已被访问过,则继续尝试下一个位置
if (visited[x][y] == 1) {
continue;
}
// 如果移动到了目标位置,则更新答案为当前步数加1,并输出答案,然后程序结束
if (x == x2 - 1 && y == y2 - 1) {
ans = tmp.step + 1;
printf("%d\n", ans);
return 0;
}
// 将新的位置加入队列,并将visited数组中对应的位置标记为已访问
que.push(node(x, y, tmp.step + 1));
visited[x][y] = 1;
}
}
}
最后,如果队列为空,表示无法找到到达目标位置的路径,输出ans(初始值为-1)。
这段代码的时间复杂度为O(nmk),其中n和m分别是地图的行数和列数,k是龙龙每分钟最多可以移动的步数。空间复杂度为O(n*m),用于存储地图和visited数组。
注意事项
- 输入时需要将坐标减1,因为数组索引是从0开始的,而地图坐标是从1开始的。
- 在判断位置是否越界时,需要考虑边界条件,即x >= 0 && x < n && y >= 0 && y < m。
- 在判断位置是否为障碍物时,需要判断map[x][y]是否等于'#'。
- 在判断位置是否已被访问过时,需要判断visited[x][y]是否等于1。
- 在将新的位置加入队列时,需要将新的位置的坐标和步数更新为tmp.x + dir_x[i] * j, tmp.y + dir_y[i] * j, tmp.step + 1。
- 在将新的位置加入队列之前,需要将visited数组中对应的位置标记为已访问,即visited[x][y] = 1。
- 在找到目标位置之后,需要及时输出答案并结束程序,即在ans被更新后,使用return 0退出循环。
- 输出答案时,需要考虑无法进圈的情况,即ans为-1的情况。
- 代码中的地图大小n和m的范围需要根据实际情况进行调整,保证不超过数组的最大长度。
- 代码中的每分钟最多移动步数k需要根据实际情况进行调整,保证不超过地图的大小。
C++完整代码(含详细注释)
有查重,改改再提交乐学!!!
#include <queue>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int dir_x[4] = { 0, 0, 1, -1 }; // 四个方向的x坐标变化
const int dir_y[4] = { 1, -1, 0, 0 }; // 四个方向的y坐标变化
struct node {
int x, y; // 当前节点的坐标
int step; // 到达当前节点的步数
node(int _x, int _y, int _step) : x(_x), y(_y), step(_step) {} // 构造函数
};
queue<node> que; // 定义一个队列用于BFS搜索
int visited[1000][1000]; // 记录节点是否被访问过的数组
char map[1000][1000]; // 存储地图的数组
int main() {
int n, m, k; // 地图大小n和m,每分钟最多移动步数k
int x1, y1, x2, y2; // 初始位置和目标位置的坐标
scanf("%d%d%d", &n, &m, &k); // 读取输入
for (int i = 0; i < n; ++i) {
scanf("%s", &map[i]); // 读取地图的每一行
}
scanf("%d%d%d%d", &x1, &y1, &x2, &y2); // 读取初始位置和目标位置
que.push(node(x1 - 1, y1 - 1, 0)); // 将初始位置加入队列
int ans = -1; // 初始化答案为-1,表示无法进圈
while (!que.empty()) {
node tmp = que.front(); // 取出队列中的第一个节点
que.pop(); // 弹出队列中的第一个节点
for (int i = 0; i < 4; ++i) { // 尝试四个方向移动
for (int j = 1; j < k + 1; j++) { // 尝试每个方向移动k步
int x = tmp.x + dir_x[i] * j; // 计算新的x坐标
int y = tmp.y + dir_y[i] * j; // 计算新的y坐标
// 如果移动后的位置越界或是障碍物,则停止继续移动
if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == '#') {
break;
}
// 如果位置已被访问过,则继续尝试下一个位置
if (visited[x][y] == 1) {
continue;
}
// 如果移动到了目标位置,则更新答案为当前步数加1,并输出答案,然后程序结束
if (x == x2 - 1 && y == y2 - 1) {
ans = tmp.step + 1;
printf("%d\n", ans);
return 0;
}
// 将新的位置加入队列,并将visited数组中对应的位置标记为已访问
que.push(node(x, y, tmp.step + 1));
visited[x][y] = 1;
}
}
}
printf("%d\n", ans); // 输出答案
return 0;
}