📅  最后修改于: 2023-12-03 15:12:46.054000             🧑  作者: Mango
题目链接:https://www.luogu.com.cn/problem/P1179
给定一个大小为 $n$ 的二维平面,平面上有 $m$ 个门和 $k$ 个障碍物,其中每个门可以通向一个确定的位置,每个障碍物为一个点。同时设定一个起始点,问从起始点出发,是否能够经过所有的门,且路程尽量短。如果能够经过,输出需要行动的最短路程,否则输出不可行。
第一行输入三个参数 $n, m, k$,其中 $n$ 表示平面的边长,$m$ 表示平面中的门的数量,$k$ 表示平面中的障碍物数量。
接下来 $m$ 行,每行包含 $3$ 个整数 $x, y, z$,表示一个门的坐标 $(x, y)$ 和该门通向的位置,通向的位置坐标从 $1$ 到 $n^2$。
接下来 $k$ 行,每行包含两个整数 $x, y$,表示一个障碍物的坐标 $(x, y)$。
最后一行,包含两个整数 $x, y$,表示起始点的坐标。
输出只有一行,如果存在一条可以经过所有门的路径,则输出该路径的最短距离,否则输出 NO。
3 2 2 2 2 1 3 3 3 1 1 1 3 3 1 2 2
10
因为随着路程的增加,可能会经过之前经过过的门,所以无法使用简单的贪心思路求解。这里使用 Dijkstra 算法求解最短路程。
将起始点入队,然后从队列中出队一个点作为当前点,再将该点顶点所连接的边加入到优先级队列中。每次取出距离最短的边进行更新,直到队列为空,输出最短距离。
因为门不仅包含其位置,同时还包含目的地位置,所以我们需要将该门的位置和目的地位置都加入到邻接表中。
另外,因为门是双向通行的,所以我们需要在邻接表中加上反向路径。
import heapq
class Edge:
def __init__(self, to, cost):
self.to = to
self.cost = cost
def dijkstra(s, edges):
q = []
heapq.heappush(q, (0, s))
d = [float('inf')] * (n * n + m)
d[s] = 0
while q:
p = heapq.heappop(q)
v = p[1]
if d[v] < p[0]:
continue
for e in edges[v]:
if d[e.to] > d[v] + e.cost:
d[e.to] = d[v] + e.cost
heapq.heappush(q, (d[e.to], e.to))
return d
n, m, k = map(int, input().split())
edges = [[] for _ in range(n * n + m)]
w = [list(map(int, input().split())) for _ in range(m)]
for i in range(m):
x, y, z = w[i]
edges[(x - 1) * n + y].append(Edge(z + n * n - 1, 1))
edges[z + n * n - 1].append(Edge((x - 1) * n + y, 1))
obstacle = [tuple(map(int, input().split())) for _ in range(k)]
start = tuple(map(int, input().split()))
for i, j in obstacle:
for k2 in range(m):
if w[k2][0] == i and w[k2][1] == j:
break
else:
edges[(i - 1) * n + j].append(Edge(k2 + n * n - 1, 1))
edges[k2 + n * n - 1].append(Edge((i - 1) * n + j, 1))
for i in range(m):
x, y, z = w[i]
for j in range(m):
if i == j:
continue
x_, y_, z_ = w[j]
dist = abs(x - x_) + abs(y - y_)
edges[i + n * n].append(Edge(j + n * n, dist))
edges[j + n * n].append(Edge(i + n * n, dist))
for i in range(m):
x, y, z = w[i]
for x_ in range(1, n + 1):
dist = abs(x - x_) + abs(y - 1)
if dist == 0:
continue
edges[i + n * n].append(Edge((x_ - 1) * n, dist))
edges[(x_ - 1) * n].append(Edge(i + n * n, dist))
for y_ in range(1, n + 1):
dist = abs(x - 1) + abs(y - y_)
if dist == 0:
continue
edges[i + n * n].append(Edge(y_ - 1, dist))
edges[y_ - 1].append(Edge(i + n * n, dist))
d = dijkstra((start[0] - 1) * n + start[1], edges)
ans = float('inf')
for i in range(m):
ans = min(ans, d[i + n * n])
if ans == float('inf'):
print("NO")
else:
print(ans)
Dijkstra 算法时间复杂度为 $O((m + n^2) \log{(m + n^2)})$,因为需要建立邻接表,邻接表建立的时间复杂度为 $O(m + n^2)$,空间复杂度为 $O(m + n^2)$。因此,程序的总时间复杂度为 $O((m + n^2) \log{(m + n^2)})$,总空间复杂度为 $O(m + n^2)$。
本题虽然题目的表述显得较为复杂,但其本质为求从起始点出发,经过所有门并且路程尽量短的问题。可以使用 Dijkstra 算法求解本问题。