问题
问题描述
小蓝有一天误入了一个混境之地。
好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:
- 混境之地的大小为 n⋅m,其中 # 表示不可通过的墙壁,
.
表示可以走的路,V
表示圣泉。 - 他现在所在位置的坐标为 (A,B),而这个混境之地出口的坐标为 (C,D),当站在出口时即表示可以逃离混境之地。
- 在圣泉处每一分钟可以恢复一点体力,无上限。
坏消息是:
- 小蓝仅剩下 E 点能量,能量值不可小于 0 。
小蓝可以往上下左右四个方向行走,每行走一次消耗一点能量,耗时一分钟。
小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,则输入所需要的最少时间 ,反之输出 No
。
输入格式
第 1 行输入两个正整数 n,m ,表示混境之地的大小。
第 2 行输入四个正整数 A,B,C,D ,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。
第 3 行至第 n+2行,每行 m 个字符,表示混境之地的地图,其中 #
表示不可通过的墙壁, .
表示普通的道路, V
表示圣泉。
最后一行一个正整数 E ,代表剩余的能量值。
输出格式
输出数据共一行:
- 若小蓝可以逃离混境之地,则输出所需要的最少时间。
- 若小蓝无法逃离混境之地,则输出
No
。
样例输入1
5 5
1 1 5 5
...#.
..#..
#....
V..#.
...#.
5
样例输出1
19
样例解释1
如图所示,绿色方块表示可以走的路,红色方块表示墙,蓝色圆圈小蓝当前所在的位置,橙色圆圈为终点,黄色方块表示圣泉。
从 (1,1)到 (5,5) 的一条可行道路为: (1,1)→(1,2)→(2,2)→(3,2)→(4,2)→(4,1) 休息七个单位时间后 →(4,2)→(4,3)→(3,3)→(3,4)→(3,5)→(4,5)→(5,5)。
样例输入2
5 5
1 1 5 5
...#.
..#..
#....
V..##
...#.
200
样例输出2
No
样例解释2
如图所示,绿色方块表示可以走的路,红色方块表示墙,蓝色圆圈小蓝当前所在的位置,橙色圆圈为终点,黄色方块表示圣泉。
可以证明,不存在一条合法路径从起点到终点。
数据范围
对于所有测试样例, 1 ≤ n,m ≤ 1000 ,1 ≤ E ≤ 。
数据保证起点和终点一定为普通道路。
解题思路
这个问题是一个很典型的图搜索问题,其结合了广度优先搜索(BFS)和状态管理的问题。我们需要通过BFS来探索所有可能的路径,并在搜索过程中考虑小蓝的体力状态。
由于圣泉的存在,我们需要从起点以及终点出发,遍历它的各个点到起点以及终点要消耗的能量。如果从起点到达终点要消耗的能量小于或等于E,此时可以直接到达终点,如果大于E,那么可以到达圣泉补充能量之后再到达终点。
进行广度搜索时,首先将初始点加入队列并且打标记,此时我们的队列为空,进入遍历,此时,我们首先将队列最左边的元素弹出,判断是否能够进行上下左右移动,移动到每一个点的时候打上标记,确保每一个能够到达点都可到达且不重复,移动到的点需要的能量等于上一点的能量加上1,此时再将移动到的点入队列。
初始化
在读入a , b , c , d时,由于输入是从1开始的,我决定使用lambda函数将他们各减去1,使得它从0开始,接着我们要建立一个列表来存储地图的信息。
n , m = map(int , input().split())
a , b , c , d = map(lambda x : x - 1 , map(int , input().split()))
G = []
for i in range(n):
G.append(list(input()))
e = int(input())
其中n , m代表的是地图的行、列,e代表剩余的能量。为了存储起点到各个点需要的能量,以及终点到各个点的能量,我们初始化列表dist1以及dist2来存储到各个点消耗的能量,设置初始值为INF,若遍历结束后仍为INF,代表无法到达。
INF = float('inf')
dist1 = [[INF] * m for _ in range(n)]
dist2 = [[INF] * m for _ in range(n)]
限制条件
我们在进行上下左右移动的时候要确保每一个点都被遍历到的同时不超出列表的索引(即到达的点必须大于等于0且在行数小于n , 在列数小于m),且该点没用被打标记(被标记过的点代表已经被遍历过),同时我们要保证到达的点不能是障碍物。
输出选择
首先我们对起点开始的dist1列表进行分析,当我们到达终点需要消耗的能量小于E时,此时我们可以直接到达终点,直接输出到达终点需要消耗的能量即可(由于每一步消耗的能量和消耗的时间相同,直接到达的情况下输出消耗的能量等于消耗的时间)。在前面不成立的情况下,我们计算从起点到达圣泉加上从终点到达圣泉的最小值,如果此时得到的结果为INF代表无法到达圣泉,也无法直接到达终点,此时直接输出No即可,以上两种情况都不成立的前提下代表能够通过圣泉补充能量再到达终点,此时我们考虑需要消耗的时间数:
1、我们从起点以及终点到达圣泉需要消耗的时间,分别为start_time(起点开始) 、 end_time(终点开始)
2、在圣泉补充能量消耗的时间(一开始拥有一定的能量)(start_time + end_time - E)
由上可得,总共需要消耗2 * (start_time + end_time) - E的时间。
完整代码
from collections import deque
INF = float('inf')
n , m = map(int , input().split())
a , b , c , d = map(lambda x : x - 1 , map(int , input().split()))
G = []
for i in range(n):
G.append(list(input()))
e = int(input())
dist1 = [[INF] * m for _ in range(n)]
dist2 = [[INF] * m for _ in range(n)]
def bfs(x , y , dist):
mark = [[1] * m for _ in range(n)]
dist[x][y] = 0
mark[x][y] = 0
q = deque()
q.append([x , y])
while len(q) != 0:
x , y = q.popleft()
for deletax , deletay in [(1 , 0) , (0 , 1) , (-1 , 0) , (0 , -1)]:
xx , yy = deletax + x , deletay + y
if 0 <= xx < n and 0 <= yy < m and mark[xx][yy] and G[xx][yy] != '#':
q.append([xx , yy])
dist[xx][yy] = dist[x][y] + 1
mark[xx][yy] = 0
bfs(a , b , dist1)
bfs(c , d , dist2)
deplete = INF
if dist1[c][d] <= e:
print(dist1[c][d])
else:
for i in range(n):
for j in range(m):
if G[i][j] == 'V':
deplete = min(deplete , dist1[i][j] + dist2[i][j])
if deplete == INF:
print('No')
else:
print(2 * deplete - e)