787.K站中转内最便宜的航班
题目描述
有n个城市通过一些航班连接。给你一个数组filghts,其中filghts[i] = [fromi, toi, pricei],表示该航班从城市fromi开始,价格pricei抵达toi。
现在给定所有的城市和航班,以及出发城市src和目的地dst,你的任务是找到一条最多经过k站中转的路线,使得从src到dst的价格最便宜,并返回该价格。如果不存在这样的路线,则输出-1。
示例1
输入:n = 3, edges = [[0, 1, 100], [1, 2, 100], [0, 2, 500]], src = 0, dst = 2, k = 1
输出:200
解释:城市航班图如下:
从城市0到城市2在1站中转以内的最便宜价格是200,如图中红色所示。
示例2
输入:n = 3, edges = [[0, 1, 100], [1, 2, 100], [0, 2, 500]], src = 0, dst = 2, k = 0
输出:500
解释:城市航班图见示例1,从城市0到城市2在0站中转以内的最便宜价格是500,如图中蓝色所示。
提示
- 1 <= n <= 100
- 0 <= filghts.length <= (n*(n-1)/2)
- filghts[i].length == 3
- 0 <= fromi, toi < n
- fromi != toi
- 1 <= pricei <= 104
- 航班没有重复,且不存在自环
- 0 <= src, dst, k < n
- src != dst
思路:Dijkstra、Bellman Ford、DFS
Dijkstra
求解最低价格(可视为最短距离),很自然就会想到Dijkstra算法。但是由于有中转站数量的限制,所以需要调整Dijkstra算法以适应中转站的条件。
class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
# Dijkstra
import heapq
if len(flights) == 0:
return -1
# 建图
graph = defaultdict(list)
for fromi, toi, pricei in flights:
graph[fromi].append([toi, pricei])
# 构建访问集合,避免重复访问城市
visited = set()
# 构建城市队列
q = [src]
while q:
cur = q.pop()
visited.add(cur)
for toi, pricei in graph[cur]:
if toi not in visited:
q.append(toi)
# 遍历结束后,如果目的地不在访问集合中,说明遍历失败
if dst not in visited:
return -1
# 构建二维列表,表示到达的城市、所花费的价格以及中转次数
pq = [[0, -1, src]]
while pq:
price, passed, cur = heapq.heappop(pq)
if cur == dst:
return price
for toi, pricei in graph[cur]:
if passed + 1 <= k:
heapq.heappush(pq, [price+pricei, passed+1, toi])
return -1
Bellman Ford
这个题是Bellman-Ford算法的模板题。该算法,每一次迭代所有的边,则第i次迭代得到的是最多走i步时,从src到各个节点的最短路径。该算法基于动态规划。
class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
# Bellman Ford
max_price = float('inf')
price = [[max_price] * n for i in range(k+2)]
price[0][src] = 0
for i in range(1, k+2):
price[i][src] = 0
for fromi, toi, pricei in flights:
price[i][toi] = min(price[i][toi], price[i-1][fromi] + pricei)
return -1 if price[k+1][dst] >= max_price else price[k+1][dst]
DFS+记忆化
直接递归会超时,所以要用记忆化递归。
class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
# DFS+记忆化
graph = defaultdict(dict)
for fromi, toi, pricei in flights:
graph[fromi][toi] = pricei
@lru_cache(None)
def dfs(city, remain):
if city == dst:
return 0
if not remain:
return inf
remain -= 1
ans = inf
for nxt in graph[city]:
ans = min(ans, dfs(nxt, remain) + graph[city][nxt])
return ans
res = dfs(src, k+1)
return res if res != inf else -1