方格迷宫

方格迷宫

给定一个 nm 列的方格矩阵。

行从上到下依次编号为 1n,列从左到右依次编号为 1m

i 行第 j 列的方格表示为 (i,j)

矩阵中的方格要么是空地(用 . 表示),要么是陷阱(用 # 表示)。

初始时,你位于方格 (x1,y1),你需要前往方格 (x2,y2)

每次移动,你可以任选上、下、左、右四个方向之一,并沿该方向移动 1k 步。

从一个方格移动至相邻方格视为一步。

但是,你要保证在你的移动过程中不能走出矩阵,也不能进入陷阱方格。

请你计算从方格 (x1,y1) 移动至方格 (x2,y2),所需要的最少移动次数

保证方格 (x1,y1) 和方格 (x2,y2) 都是空地。

方格 (x1,y1) 和方格 (x2,y2) 可能是同一个方格。

注意:注意区分本题中移动次数与移动步数的区别。

输入格式

第一行包含三个整数 n,m,k

接下来 n 行,每行包含 m 个字符,其中第 i 行第 j 个字符,要么是 .,表示方格 (i,j) 是空地;要么是 #,表示方格 (i,j) 是陷阱。

最后一行包含四个整数 x1,y1,x2,y2

输出格式

一个整数,表示所需要的最少移动次数

如果无法从方格 (x1,y1) 移动至方格 (x2,y2),则输出 1

数据范围

6 个测试点满足 1n,m10
所有测试点满足 1n,m,k10001x1,x2n1y1,y2m

输入样例1:

3 4 4
....
###.
....
1 1 3 1

输出样例1:

3

输入样例2:

3 4 1
....
###.
....
1 1 3 1

输出样例2:

8

输入样例3:

2 2 1
.#
#.
1 1 2 2

输出样例3:

-1

 

解题思路

  很容易想到bfs,然后在枚举4个方向时再枚举这个方向要走的长度,因此这样做的时间复杂度为O(nmk)。这题有个比较巧妙的剪枝,假设当前位置是(x,y),到该位置的距离是dx,y,在同一个方向上枚举要走的长度,当枚举到位置(x+dx,y+dy)且有d(x+dx,y+dy)<dx,y+1,那么之后的格子可以用(x+dx,y+dy)这个位置来更新,因为d(x+dx,y+dy)<dx,y+1,因此有d(x+dx,y+dy)+1dx,y+1,因此用(x+dx,y+dy)来更新同一个方向之后的格子最小距离不会变大。所以在枚举的过程中发现d(x+dx,y+dy)<dx,y+1直接break就好了。

  加上这个剪枝后时间复杂度就变成了O(nm)。可以证明在同一个方向上每个格子最多会被枚举到2次(一次是第一次枚举到该格子并更新距离,另外一次是从距离更大的格子枚举到并break),因此4个方向所有格子一共最多被枚举8nm次。

  假设距离为d的格子已经沿着一个方向把一些格子更新成了d+1,此时再从队列中弹出一个格子(下图红色格子),根据bfs的性质这个格子的距离一定d,因此当这个格子枚举到距离为d的格子或者已经被更新为d+1的格子时就会break掉。

  其中上图右边的红色格子更具体来说应该是d+1。首先不可能是d,否则下面距离为d的白色格子走到这个红色格子就会break掉,就不会更新到上面那些距离为d+1的格子了。而红色格子又是距离为d的白色格子上面的格子,因此就会被更新为d+1,所以红色格子的距离应该就是d+1

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef pair<int, int> PII;
 5 
 6 const int N = 1010;
 7 
 8 char g[N][N];
 9 int dist[N][N];
10 PII q[N * N];
11 int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
12 
13 int main() {
14     int n, m, k, x1, y1, x2, y2;
15     scanf("%d %d %d", &n, &m, &k);
16     for (int i = 0; i < n; i++) {
17         scanf("%s", g + i);
18     }
19     scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
20     x1--, y1--, x2--, y2--;
21     memset(dist, 0x3f, sizeof(dist));
22     dist[x1][y1] = 0;
23     int hh = 0, tt = -1;
24     q[++tt] = {x1, y1};
25     while (hh <= tt) {
26         PII t = q[hh++];
27         for (int i = 0; i < 4; i++) {
28             for (int j = 1; j <= k; j++) {
29                 int x = t.first + dx[i] * j, y = t.second + dy[i] * j;
30                 if (x < 0 || x >= n || y < 0 || y >= m) break;
31                 if (g[x][y] == '#') break;
32                 if (dist[x][y] < dist[t.first][t.second] + 1) break;
33                 if (dist[x][y] > dist[t.first][t.second] + 1) {
34                     dist[x][y] = dist[t.first][t.second] + 1;
35                     q[++tt] = {x, y};
36                 }
37             }
38         }
39     }
40     printf("%d", dist[x2][y2] == 0x3f3f3f3f ? -1 : dist[x2][y2]);
41     
42     return 0;
43 }
复制代码

 

参考资料

  AcWing 4943. 方格迷宫(第二届ACC(AcWing Cup)全国联赛):https://www.acwing.com/video/4683/

posted @   onlyblues  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-04-04 牛奶工厂
Web Analytics
点击右上角即可分享
微信分享提示