###### tags: `Leetcode` `medium` `graph` `heap` `dynamic programming` `python` `c++` # 787. Cheapest Flights Within K Stops ## [題目連結:] https://leetcode.com/problems/cheapest-flights-within-k-stops/ ## 題目: There are n cities connected by some number of flights. You are given an array ```flights``` where ```flights[i] = [fromi, toi, pricei]``` indicates that there is a flight from city ```fromi``` to city ```toi``` with cost ```pricei```. You are also given three integers ```src```, ```dst```, and ```k```, return **the cheapest price** from ```src``` to ```dst``` with at most ```k``` stops. If there is no such route, return ```-1```. ![](https://i.imgur.com/0hvfugC.png) ![](https://i.imgur.com/ekLiK9j.png) ![](https://i.imgur.com/J1Tqa3x.png) ## 解題想法: * 此題為求從src到dst的最少需要花多少錢,**最多**可以中轉k個休息站。 * 因為k=1,代表可以有一個休息站 * 走A->B->C,可視為走k+1步 ## 法1: graph * **Dijkstra**: 指定一點到其餘各點的最短路徑 * 正常Dijkstra為greedy,紀錄每點是否已拜訪過並逐一取出最小權重更新 * 但此題為變化,需利用k步判斷,所以就不採用額外visited數組紀錄每點是否已經拜訪過 * 改用字典紀錄起始點與已走幾步作為拜訪依據 * **dic**=defaultdict(lambda: float('inf')) * **key:(src到其他位置,經由幾步到達)** * **val:cost** * 補充知識,defaultdict的默認值: item=defaultdict(lambda: "預設") * 編寫流程: * use **binary heap**: time: O((|e|+|v|)logV) * Step1: 建立邊 ``` python= edge=defaultdict(list) for u,v,w in flights: edge[u].append((v,w)) ``` * Step2: 初始所需工具 ``` python= dic=defaultdict(lambda: float('inf')) #key:(src到其他位置,經由幾步到達) val:cost que=[(0, src, 0)] #(目前cost,起點,已經走了幾步) ``` * Step3-1: while迴圈每次pop出que中權重最小的 ``` python= while que: cost, curPos, step=heappop(que) #每次pop出cost最小的 ``` * Step3-2: 判斷步數是否合法 ``` python= if step>k+1: #若已經超過距離就略過 continue ``` * Step3-3: 判斷終止條件 ``` python= if curPos==dst: return cost ``` * Step3-4: 更新鄰居 ``` python= for neighber, weight in edge[curPos]: tmpCost=cost+weight #若權重加總小於先前dic中紀錄的,即可更新並加入que if tmpCost<dic[(neighber,step+1)]: heappush(que, (tmpCost, neighber, step+1)) dic[(neighber, step+1)]=tmpCost ``` * Step4: 跳出while結束 ``` python= return -1 ``` ## Python: ``` python= from collections import defaultdict from heapq import heappush, heappop 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 """ #dijkstra #use binary heap: time: O((|e|+|v|)logV) edge=defaultdict(list) #建立邊 for u,v,w in flights: edge[u].append((v,w)) #key:(src到其他位置,經由幾步到達) val:cost #defaultdict的默認值: item=defaultdict(lambda: "預設") #或用一般dict.get的用法: dic.get(key,val) val為選填 若該key不存在val為預設值 dic=defaultdict(lambda: float('inf')) que=[(0, src, 0)] #目前cost,起點,已經走了幾步 while que: cost, curPos, step=heappop(que) #每次pop出cost最小的 #注意: 因為k=1 代表可以有一個休息站 走A->B->C 可視為走k+1步 if step>k+1: #若已經超過距離就略過 continue if curPos==dst: return cost #更新鄰居 for neighber, weight in edge[curPos]: tmpCost=cost+weight if tmpCost<dic[(neighber,step+1)]: heappush(que, (tmpCost, neighber, step+1)) dic[(neighber, step+1)]=tmpCost return -1 if __name__=='__main__': n =5 flights =[[0,1,5],[1,2,5],[0,3,2],[3,1,2],[1,4,1],[4,2,1]] src =0 dst =2 k =2 result=Solution() ans=result.findCheapestPrice(n,flights,src,dst,k) print(ans) ``` ## 法2: 使用Dynamic Programming * **Bellman Ford** * **dp[i][j]: 從起始位置,走i步能到達j位置的最低價格** * 初始: * dp[i][src]=0 (i=0~k+1): 起始位置走i步到起始位置,cost=0 * 轉移方程式: * 對於每組flights=[curPos, Neighber, weight] * 每次**更新鄰居**: * 對於走i步能到達neighber :**dp[i][neighber]** * dp[i][neighber]=min(原本走i步能到達neighber or **走i-1步達到當前位置curPos,再加上curPos到neighber的weight**) * dp[i][neighber]=min(dp[i][neighber], dp[i-1][curPos]+weight) ## C++: ``` cpp= class Solution { public: int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) { vector<vector<long int>> dp(k+2, vector<long int>(n, INT_MAX)); dp[0][src]=0; for (int i=1; i<k+2; i++){ dp[i][src]=0; for (auto item: flights){ int curPos=item[0], neighber=item[1], weight=item[2]; dp[i][neighber]=min(dp[i][neighber], dp[i-1][curPos]+weight); } } return (dp[k+1][dst]==INT_MAX)? -1: dp[k+1][dst]; } }; ```