04115: 鸣人和佐助
bfs, http://cs101.openjudge.cn/practice/04115/
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?

已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?
输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10 后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。
样例输入
样例输入1
4 4 1
#@##
**##
###+
****
样例输入2
4 4 2
#@##
**##
###+
****样例输出
样例输出1
6
样例输出2
4思路:稍复杂的bfs问题。visited需要维护经过时的最大查克拉数,只有大于T值时候才能通过,然后就是常见bfs。
# 夏天明 元培学院
from collections import deque
M, N, T = map(int, input().split())
graph = [list(input()) for i in range(M)]
direc = [(0,1), (1,0), (-1,0), (0,-1)]
start, end = None, None
for i in range(M):
for j in range(N):
if graph[i][j] == '@':
start = (i, j)
def bfs():
q = deque([start + (T, 0)])
visited = [[-1]*N for i in range(M)]
visited[start[0]][start[1]] = T
while q:
x, y, t, time = q.popleft()
time += 1
for dx, dy in direc:
if 0<=x+dx<M and 0<=y+dy<N:
if (elem := graph[x+dx][y+dy]) == '*' and t > visited[x+dx][y+dy]:
visited[x+dx][y+dy] = t
q.append((x+dx, y+dy, t, time))
elif elem == '#' and t > 0 and t-1 > visited[x+dx][y+dy]:
visited[x+dx][y+dy] = t-1
q.append((x+dx, y+dy, t-1, time))
elif elem == '+':
return time
return -1
print(bfs())# 2300011075 苏王捷 工学院
'''
这段代码是一个迷宫求解的问题。主要使用了广度优先搜索算法来找到从起点到终点的最短路径。
首先,代码导入了deque模块来实现队列数据结构。
然后,定义了一个Node类,表示迷宫中的一个节点。它有四个属性:x和y表示节点的坐标,
tools表示节点当前拥有的工具数,steps表示从起点到达该节点的步数。
接下来,读取输入的迷宫信息,包括迷宫的大小M和N,以及可以使用的工具数T。maze是一个二维列表,
表示迷宫的格子,其中'@'表示起点,'+'表示终点,'*'表示障碍物。
创建了一个visit列表,用于记录节点是否被访问过。visit是一个三维列表,
三个维度分别表示行、列和工具数。
定义了directions列表,包含四个方向的偏移量。
通过遍历迷宫,找到起点和终点的位置,并设置起点节点的属性。
使用广度优先搜索算法,通过一个队列queue来依次处理节点。从起点开始,判断四个方向的相邻节点:
如果是'*'表示可以直接通过,将其加入队列并标记为已访问;如果是'#'表示需要使用一个工具,
才能通过,判断当前节点是否还有工具可用,如果有则减少一个工具,并将该节点加入队列并标记为已访问。
如果队列为空,即无法到达终点,则输出"-1";如果找到终点,输出步数,并将flag标记为1,退出循环。
最后,判断flag是否为0,如果是说明无法找到终点,输出"-1"。
'''
from collections import deque
class Node:
def __init__(self, x, y, tools, steps):
self.x = x
self.y = y
self.tools = tools
self.steps = steps
M, N, T = map(int, input().split())
maze = [list(input()) for _ in range(M)]
visit = [[[0]*(T+1) for _ in range(N)] for _ in range(M)]
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
start = end = None
flag = 0
for i in range(M):
for j in range(N):
if maze[i][j] == '@':
start = Node(i, j, T, 0)
visit[i][j][T] = 1
if maze[i][j] == '+':
end = (i, j)
maze[i][j] = '*'
queue = deque([start])
while queue:
node = queue.popleft()
if (node.x, node.y) == end:
print(node.steps)
flag = 1
break
for direction in directions:
nx, ny = node.x+direction[0], node.y+direction[1]
if 0 <= nx < M and 0 <= ny < N:
if maze[nx][ny] == '*':
if not visit[nx][ny][node.tools]:
queue.append(Node(nx, ny, node.tools, node.steps+1))
visit[nx][ny][node.tools] = 1
elif maze[nx][ny] == '#':
if node.tools > 0 and not visit[nx][ny][node.tools-1]:
queue.append(Node(nx, ny, node.tools-1, node.steps+1))
visit[nx][ny][node.tools-1] = 1
if not flag:
print("-1")用一个特殊的bfs,除了记录位置,还要记录剩下的查克拉数量,某个位置能走的前提是,这次走到这个位置所剩下的查克拉,要比上一次来的时候多(初始为-1),其他的和正常bfs相同
# 钟明衡 物理学院
m, n, t = map(int, input().split())
M = [input() for _ in range(m)]
x = y = f = 0
for i in range(m):
for j in range(n):
if M[i][j] == '@':
x, y = i, j
f = 1
break
if f:
break
s, e = 0, 1
l = [(x, y, t)]
g = [[-1]*n for i in range(m)]
g[x][y] = t
c = 0
dx, dy = [1, 0, -1, 0], [0, 1, 0, -1]
while s != e:
c += 1
for i in range(s, e):
for j in range(4):
x, y, k = l[i][0]+dx[j], l[i][1]+dy[j], l[i][2]
if x in (-1, m) or y in (-1, n):
continue
if M[x][y] == '+':
print(c)
exit()
elif M[x][y] == '#':
if k-1 > g[x][y]:
g[x][y] = k-1
l.append((x, y, k-1))
elif M[x][y] == '*':
if k > g[x][y]:
g[x][y] = k
l.append((x, y, k))
s, e = e, len(l)
print(-1)主要是对bfs作修改,在过程中点是可以重复走的,只有查克拉数大于之前该点的查克拉数才能再次走该点,这样能保证不丢失解,用时就是看第几层遇到了‘+’
# 王昊 光华管理学院
def dijkstra(g, start_x, start_y, T):
queue = [(start_x, start_y, T)]
cost = 0
energy = [[-1] * N for _ in range(M)]
energy[start_x][start_y] = T
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
start, end = 0, 1
while start != end:
cost += 1
for i in range(start, end):
current_x, current_y, currentEng = queue[i]
for x, y in directions:
next_x = current_x + x
next_y = current_y + y
if 0 <= next_x < M and 0 <= next_y < N:
if g[next_x][next_y] == '+':
print(cost)
exit()
elif g[next_x][next_y] == '#':
if currentEng - 1 > energy[next_x][next_y]:
energy[next_x][next_y] = currentEng - 1
queue.append((next_x, next_y, currentEng - 1))
elif g[next_x][next_y] == '*':
if currentEng > energy[next_x][next_y]:
energy[next_x][next_y] = currentEng
queue.append((next_x, next_y, currentEng))
start, end = end, len(queue)
M, N, T = map(int, input().split())
g = []
start_x = 0
start_y = 0
for i in range(M):
g.append(list(input()))
if '@' in g[i]:
start_x = i
start_y = g[i].index('@')
dijkstra(g, start_x, start_y, T)
print(-1)bfs时在visited里面存储能量值(不同能量值可能多次通过同一位置)。
# 高铭泽 物理学院
from collections import deque
dire = [(-1, 0), (0, -1), (1, 0), (0, 1)]
flag = 0
ans = 0
def bfs(x, y, t):
visited = set()
global ans, flag
q = deque()
q.append((t, x, y, 0))
while q:
t, x, y, ans = q.popleft()
for dx, dy in dire:
nx = x + dx
ny = y + dy
if 0 <= nx < m and 0 <= ny < n:
if g[nx][ny] != "#":
nt = t
else:
nt = t - 1
if nt >= 0 and (nt, nx, ny) not in visited:
newans = ans + 1
if g[nx][ny]=="+":
flag = 1
return flag,newans
q.append((nt, nx, ny, newans))
visited.add((nt, nx, ny))
return flag,ans
m, n, t = map(int, input().split())
g = []
for i in range(m):
g.append(list(input()))
for i in range(m):
for j in range(n):
if g[i][j] == "@":
x = i
y = j
flag,newans=bfs(x, y, t)
if flag:
print(newans)
else:
print(-1)思路:使用bfs,令有visited字典保存走过位置的最大chakra,防止冗余
#段明远 元培
from collections import deque
di = [1,0,0,-1]; dj = [0,1,-1,0]
def bfs(mat, M, N, T):
found = 0
for i in range(M):
if found:
break
for j in range(N):
if mat[i][j] == '@':
naruto_i = i
naruto_j = j
found = 1
break
if mat[naruto_i][naruto_j] == '+':
return 0
queue = deque([(naruto_i, naruto_j, T)])
step = 0
visited = {(naruto_i, naruto_j): T}
while queue:
step += 1
for _ in range(len(queue)):
i, j, chakra = queue.popleft()
for k in range(4):
if 0<=i+di[k]<M and 0<=j+dj[k]<N and (chakra or mat[i+di[k]][j+dj[k]] != '#') and visited.get((i+di[k], j+dj[k]), -1) < chakra:
if mat[i+di[k]][j+dj[k]] == '+':
return step
queue.append((i+di[k], j+dj[k], chakra - int(mat[i+di[k]][j+dj[k]] == '#')))
visited[(i+di[k], j+dj[k])] = chakra - int(mat[i+di[k]][j+dj[k]] == '#')
return -1
M, N, T = map(int, input().split())
mat = []
for i in range(M):
mat.append(list(input()))
ans = bfs(mat, M, N, T)
print(ans)思路:heap+bfs
# 23物院宋昕杰
from heapq import *
m, n, t = map(int, input().split())
matrix = [[-1]*(n + 2)] + [[-1] + list(input()) + [-1] for _ in range(m)] + [[-1]*(n + 2)]
heap = []
x, y = 0, 0
for i in range(1, m + 1):
for j in range(1, n + 1):
if matrix[i][j] == '@':
x, y = i, j
break
heap.append((0, x, y, t))
heapify(heap)
searched = {}
while heap:
time, x, y, left = heappop(heap)
if matrix[x][y] == '+':
print(time)
exit()
elif matrix[x][y] == '#':
left -= 1
if left < 0:
continue
if matrix[x][y] == -1:
continue
if (x, y) in searched:
if searched[(x, y)] >= left:
continue
searched[(x, y)] = left
time += 1
heappush(heap, (time, x + 1, y, left))
heappush(heap, (time, x - 1, y, left))
heappush(heap, (time, x, y + 1, left))
heappush(heap, (time, x, y - 1, left))
print(-1)