leetcode 787. Cheapest Flights Within K Stops
题目描述
There are n
cities connected by m
flights. Each fight starts from city u
and arrives at v
with a price w
.
Now given all the cities and fights, together with starting city src
and the destination dst
, your task is to find the cheapest price from src
to dst
with up to k
stops. If there is no such route, output -1
.
Note:
- The number of nodes
n
will be in range[1, 100]
, with nodes labeled from0
ton - 1
. - The size of flights will be in range
[0, n * (n - 1) / 2]
. - The format of each flight will be
(src, dst, price)
. - The price of each flight will be in the range
[1, 10000]
. k
is in the range of[0, n - 1]
.- There will not be any duplicated flights or self cycles.
Difficulty: medium
787. Cheapest Flights Within K Stops
中文描述
给你有n
个城市,m
个航班,每个航班是一个三元组包含出发地u
,终点v
,价格w
的三个信息,要求你在转乘不超过k
次的情况下,从起始点src
到终点dst
的最便宜的方法。
输入格式
第一个输入n
表示城市个数,第二个输入列表edges
表示每个航班的起始点,目的地,价格的信息,第三个输入src
为出发点,第四个输入dst
为终点,第五个输入k
表示需要最多换乘k
次。
Examples:
Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Output: 200
解释:
从0到2有两种方法,可以从0 –> 1 –> 2,价格为200,或则0–>2,价格500.
因为可以转乘一次,所以最少为200Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
Output: 500
解释:
从0到2有两种方法,可以从0 –> 1 –> 2,价格为200,或则0–>2,价格500.
因为不能转乘,所以最少为500
解答思路
解法一:dfs + memory
原问题为在转机不超过k次的情况下,从src到dst的价格最少。记mid为从src起飞能到的城市之一。原问题转化为最小化在不超过k-1次的情况下,从mid到dst的价格+src到mid的价格。即 minmid{Psrc−>mid,1+Pmid−>dst,k−1} 。可以用dfs,遍历所有情况,用memory记录下经历过的转态,避免重复计算。
解法二:应用heapq
思路和上述差不多,不过每次我们都从当前费用最小的往外扩展,如果遇到满足题目要求的方法后就可以直接跳出,可以减少一些不必要的计算。
解法三:动态规划
用一个二维的dp数组,dp[i][j]表示在不超过i次转机的情况下,从j到达dst的最少费用。dp[i][j]= mink {dp[i-1][k]+ Pk−>j }。
(从自己到自己为0, Pk−>k=0,若是没有这个线路则,Pk−>j=inf )
代码
解法一
class Solution(object):
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
315ms dfs + memory
"""
# 把原始数据处理下,按起始点划分
import collections
flights_dict = collections.defaultdict(list)
for flight in flights:
flights_dict[flight[0]].append(flight[1:])
# 用来记录已经计算过的位置
memory = {}
def dfs(K, src, price):
# 如果该位置已经被访问过,直接输出结果
if (src, K) in memory:
return memory[(src, K)]
# 不能到达的情况,记录下来
if K < 0:
memory[(src, K)] = float('inf')
return float('inf')
min_price = float('inf')
# 遍历所有从该点出发的方案
for next_src in flights_dict[src]:
price += next_src[1]
if next_src[0] == dst:
a = price
else:
a = dfs(K - 1, next_src[0], 0) + price
# 选择价格最少的
min_price = min(a, min_price)
price -= next_src[1]
# 记录下来
memory[(src, K)] = min_price
return memory[(src, K)]
ans = dfs(K, src, 0)
return ans if ans != float('inf') else -1
解法二,参考了其它人写的代码详细请见Easy and Concise Solution Using Priority Queue [Java/Python]:
class Solution(object):
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
88ms heapq
"""
import heapq, collections
flights_dict = collections.defaultdict(list)
for flight in flights:
flights_dict[flight[0]].append(flight[1:])
h = [(0, src, 0)]
while h:
a = heapq.heappop(h)
if a[1] == dst:
return a[0]
for next_src in flights_dict[a[1]]:
if a[2] < K + 1:
heapq.heappush(h, (next_src[1] + a[0], next_src[0], a[2] + 1))
return -1
解法三:
class Solution(object):
def findCheapestPrice(self, n, flights, src, dst, K):
"""
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type K: int
:rtype: int
176ms dp
"""
import collections
# 记录同一个终点的不同起点和价格
flights_dict_end = collections.defaultdict(list)
for flight in flights:
flights_dict_end[flight[1]].append([flight[0], flight[2]])
# 用来记录各种情况,n个站点,还剩K次,到达目的地最少价格
dp = [[float('inf') for i in range(n)] for i in range(K + 1)]
# 初始化能直达的
for i in flights_dict_end[dst]:
dp[0][i[0]] = i[1]
# 反向推回去
for k in range(1, K + 1):
for pos in range(n):
for before in flights_dict_end[pos]:
dp[k][before[0]] = min(dp[k][before[0]], dp[k-1][pos] + before[1])
dp[k][pos] = min(dp[k][pos], dp[k-1][pos])
ans = dp[K][src]
return ans if ans != float('inf') else -1