https://leetcode.com/problems/cheapest-flights-within-k-stops/description/
X. DFS
X. DP - Bellman-Ford algorithm
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/163698/easy-java-Bellman-Ford
https://github.com/allaboutjst/airbnb/blob/master/README.md
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
.Example 1: Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 1 Output: 200 Explanation: The graph looks like this: The cheapest price from city0
to city2
with at most 1 stop costs 200, as marked red in the picture.
X. Using PriorityQueue
Time Complexity is O(ElogV) E is the length of flights and V is the number of cities
It happen to be the same idea of Dijkstra's algorithm, but we need to keep the path while explore the grape.
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/115541/Easy-and-Concise-Solution-Using-Priority-Queue-JavaPython
We don't need a visited map here, since if a city is visited multiple times(e.g. there is a circle), the distance will increase, which will decrease it's priority in PriorityQueue
The key difference with the classic Dijkstra algo is, we don't maintain the global optimal distance to each node, i.e. ignore below optimization:
We don't need a visited map here, since if a city is visited multiple times(e.g. there is a circle), the distance will increase, which will decrease it's priority in PriorityQueue
The key difference with the classic Dijkstra algo is, we don't maintain the global optimal distance to each node, i.e. ignore below optimization:
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
Map<Integer, Map<Integer, Integer>> prices = new HashMap<>();
for (int[] f : flights) {
if (!prices.containsKey(f[0])) prices.put(f[0], new HashMap<>());
prices.get(f[0]).put(f[1], f[2]);
}
Queue<int[]> pq = new PriorityQueue<>((a, b) -> (Integer.compare(a[0], b[0])));
pq.add(new int[] {0, src, k + 1});
while (!pq.isEmpty()) {
int[] top = pq.remove();
int price = top[0];
int city = top[1];
int stops = top[2];
if (city == dst) return price;
if (stops > 0) {
Map<Integer, Integer> adj = prices.getOrDefault(city, new HashMap<>());
for (int a : adj.keySet()) {
pq.add(new int[] {price + adj.get(a), a, stops - 1});
}
}
}
return -1;
}
X. BFS
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/115570/BFS-with-small-tweak-to-guarantee-k-stops
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/166443/AC.-Simple-BFS-(5ms)-No-PQ.-Beats-99-of-submissions
http://zxi.mytechroad.com/blog/dynamic-programming/leetcode-787-cheapest-flights-within-k-stops/https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/115570/BFS-with-small-tweak-to-guarantee-k-stops
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/166443/AC.-Simple-BFS-(5ms)-No-PQ.-Beats-99-of-submissions
The idea is to perform a BFS from
Code below -
src
upto only K
levels. I initially hit a Memory limit but was able to get around by adding a minPrices
check to skip exploring from a node if it does not give better result.Code below -
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
//convert flights[] to a weighted graph
//perform BFS starting from src going only max depth of K
//For Each node track the cost associated to reach that node
int min = Integer.MAX_VALUE;
int[][] graph = new int[n][n];
int[] minPrices = new int[n];
for (int[] flight : flights) {
int start = flight[0];
int end = flight[1];
graph[start][end] = flight[2];
}
Queue<Integer> q = new LinkedList<>();
Queue<Integer> prices = new LinkedList<>();
q.add(src);
prices.add(0);
while (!q.isEmpty() && K >= 0) {
K--;
int size = q.size();
for (int i = 0; i < size; i++) {
int start = q.poll();
int priceTillNow = prices.poll();
for (int j = 0; j < n; j++) {
int price = graph[start][j];
if (price > 0) { //there is a flight
int curPrice = priceTillNow + price;
int oldPrice = minPrices[j];
//If there is a cheaper flight with lesser stops do not add it to q
if (oldPrice == 0 || oldPrice > curPrice) {//pruning
minPrices[j] = curPrice;
q.add(j);
prices.add(curPrice);
if (j == dst && min > curPrice) {
min = curPrice;
}
}
}
}
}
}
return min == Integer.MAX_VALUE ? -1 : min;
}
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
unordered_map<int, vector<pair<int,int>>> g;
for (const auto& e : flights)
g[e[0]].emplace_back(e[1], e[2]);
int ans = INT_MAX;
queue<pair<int,int>> q;
q.push({src, 0});
int steps = 0;
while (!q.empty()) {
int size = q.size();
while (size--) {
int curr = q.front().first;
int cost = q.front().second;
q.pop();
if (curr == dst)
ans = min(ans, cost);
for (const auto& p : g[curr]) {
if (cost + p.second > ans) continue; // Important: prunning
q.push({p.first, cost + p.second});
}
}
if (steps++ > K) break;
}
return ans == INT_MAX ? - 1 : ans;
}
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
Map<Integer, Map<Integer, Integer>> graph = parseGraph(flights);
Queue<DestPrice> queue = new ArrayDeque<>();
addNeighborToQueue(graph.get(src), new DestPrice(0, 0), queue);
int stop = 0;
boolean canReachDest = false;
int minPrice = Integer.MAX_VALUE;
while (stop <= K) {
int size = queue.size();
for (int i = 0; i < size; i++) {
DestPrice destPrice = queue.poll();
if (destPrice.price >= minPrice) {
continue;
}
if (destPrice.dest == dst) {
canReachDest = true;
minPrice = destPrice.price; // Math.min(minPrice, destPrice.price);
continue;
}
if (stop <= K) {
addNeighborToQueue(graph.get(destPrice.dest), destPrice, queue);
}
}
++stop;
}
return canReachDest ? minPrice : -1;
}
public static class DestPrice {
public final int dest;
public final int price;
public DestPrice(int dest, int price) {
super();
this.dest = dest;
this.price = price;
}
}
private void addNeighborToQueue(Map<Integer, Integer> nbs, DestPrice destPrice, Queue<DestPrice> queue) {
if (nbs == null)
return;
for (Entry<Integer, Integer> entry : nbs.entrySet()) {
queue.add(new DestPrice(entry.getKey(), destPrice.price + entry.getValue()));
}
}
private Map<Integer, Map<Integer, Integer>> parseGraph(int[][] flights) {
Map<Integer, Map<Integer, Integer>> graph = new HashMap<>();
for (int i = 0; i < flights.length; i++) {
graph.computeIfAbsent(flights[i][0], v -> new HashMap<>()).put(flights[i][1], flights[i][2]);
}
return graph; //
}
X. DFS
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
g_.clear();
for (const auto& e : flights)
g_[e[0]].emplace_back(e[1], e[2]);
vector<int> visited(n, 0);
visited[src] = 1;
int ans = INT_MAX;
dfs(src, dst, K + 1, 0, visited, ans);
return ans == INT_MAX ? - 1 : ans;
}
private:
unordered_map<int, vector<pair<int,int>>> g_;
void dfs(int src, int dst, int k, int cost, vector<int>& visited, int& ans) {
if (src == dst) {
ans = cost;
return;
}
if (k == 0) return;
for (const auto& p : g_[src]) {
if (visited[p.first]) continue; // do not visit the same city twice.
if (cost + p.second > ans) continue; // IMPORTANT!!! prunning
visited[p.first] = 1;
dfs(p.first, dst, k - 1, cost + p.second, visited, ans);
visited[p.first] = 0;
}
}
https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/207128/Two-Java-Solutions-one-is-DP-and-the-other-is-Dijkstra
Time Complexity for this solution is O(KN), k is stop and N is the number of cities
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
//dp[i][j] denotes the cheapest price within i-1 stops, stop in j city
long[][] dp = new long[K+2][n];
for (long[] d : dp) Arrays.fill(d, Integer.MAX_VALUE);
dp[0][src] = 0;
for (int i = 1; i < K+2; i++) {
dp[i][src] = 0;
for (int[] f : flights) {
dp[i][f[1]] = Math.min(dp[i][f[1]], dp[i-1][f[0]] + f[2]);
}
}
return dp[K+1][dst] == Integer.MAX_VALUE ? -1 : (int)dp[K+1][dst];
}
http://www.cnblogs.com/grandyang/p/9109981.html
核心思想还是用的动态规划Dynamic Programming,最核心的部分就是松弛操作Relaxation,也就是DP的状态转移方程。这里我们使用一个二维DP数组,其中dp[i][j]表示最多飞i次航班到达j位置时的最少价格,那么dp[0][src]初始化为0,因为飞0次航班的价格都为0,转机K次,其实就是飞K+1次航班,我们开始遍历,i从1到K+1,每次dp[i][src]都初始化为0,因为在起点的价格也为0,然后即使遍历所有的航班x,更新dp[i][x[1]],表示最多飞i次航班到达航班x的目的地的最低价格,用最多飞i-1次航班,到达航班x的起点的价格加上航班x的价格之和,二者中取较小值更新即可
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) { vector<vector<int>> dp(K + 2, vector<int>(n, 1e9)); dp[0][src] = 0; for (int i = 1; i <= K + 1; ++i) { dp[i][src] = 0; for (auto x : flights) { dp[i][x[1]] = min(dp[i][x[1]], dp[i - 1][x[0]] + x[2]); } } return (dp[K + 1][dst] >= 1e9) ? -1 : dp[K + 1][dst]; }
我们可以稍稍优化下上面解法的空间复杂度,使用一个一维的DP数组即可,具体思路没有啥太大的区别,参见代码如下:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
vector<int> dp(n, 1e9);
dp[src] = 0;
for (int i = 0; i <= K; ++i) {
vector<int> t = dp;
for (auto x : flights) {
t[x[1]] = min(t[x[1]], dp[x[0]] + x[2]);
}
dp = t;
}
return (dp[dst] >= 1e9) ? -1 : dp[dst];
}
dp[k][i]: min cost from src to i taken up to k flights (k-1 stops)
init: dp[0:k+2][src] = 0
transition: dp[k][i] = min(dp[k-1][j] + price[j][i])
ans: dp[K+1][dst]
Time complexity: O(k * |flights|) / O(k*n^2)
Space complexity: O(k*n) -> O(n)
w/o space compression O(k*n)
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
final int kInfCost = 1<<30;
int[] cost = new int[n];
Arrays.fill(cost, kInfCost);
cost[src] = 0;
for (int i = 0; i <= K; ++i) {
int[] tmp = cost.clone();
for(int[] p: flights)
tmp[p[1]] = Math.min(tmp[p[1]], cost[p[0]] + p[2]);
cost = tmp;
}
return cost[dst] >= kInfCost ? -1 : cost[dst];
}
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
int INF = 0x3F3F3F3F;
int[] cost = new int[n];
Arrays.fill(cost, INF);
cost[src] = 0;
int ans = cost[dst];
for(int i = k; i >= 0; i--){
int[] cur = new int[n];
Arrays.fill(cur, INF);
for(int[] flight : flights){
cur[flight[1]] = Math.min(cur[flight[1]], cost[flight[0]] + flight[2]);
}
cost = cur;
ans = Math.min(ans, cost[dst]);
}
return ans == INF ? -1 : ans;
}
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
constexpr int kInfCost = 1e9;
vector<vector<int>> dp(K + 2, vector<int>(n, kInfCost));
dp[0][src] = 0;
for (int i = 1; i <= K + 1; ++i) {
dp[i][src] = 0;
for (const auto& p : flights)
dp[i][p[1]] = min(dp[i][p[1]], dp[i-1][p[0]] + p[2]);
}
return dp[K + 1][dst] >= kInfCost ? -1 : dp[K + 1][dst];
}
C++ w/ space compression O(n)
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
final int kInfCost = 1<<30;
int[] cost = new int[n];
Arrays.fill(cost, kInfCost);
cost[src] = 0;
for (int i = 0; i <= K; ++i) {
int[] tmp = cost.clone();
for(int[] p: flights)
tmp[p[1]] = Math.min(tmp[p[1]], cost[p[0]] + p[2]);
cost = tmp;
}
return cost[dst] >= kInfCost ? -1 : cost[dst];
}
Solution 1: DFS
C++ w/o prunning TLE
C++ w/ prunning
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
g_.clear();
for (const auto& e : flights)
g_[e[0]].emplace_back(e[1], e[2]);
vector<int> visited(n, 0);
visited[src] = 1;
int ans = INT_MAX;
dfs(src, dst, K + 1, 0, visited, ans);
return ans == INT_MAX ? - 1 : ans;
}
private:
unordered_map<int, vector<pair<int,int>>> g_;
void dfs(int src, int dst, int k, int cost, vector<int>& visited, int& ans) {
if (src == dst) {
ans = cost;
return;
}
if (k == 0) return;
for (const auto& p : g_[src]) {
if (visited[p.first]) continue; // do not visit the same city twice.
if (cost + p.second > ans) continue; // IMPORTANT!!! prunning
visited[p.first] = 1;
dfs(p.first, dst, k - 1, cost + p.second, visited, ans);
visited[p.first] = 0;
}
}
https://github.com/allaboutjst/airbnb/blob/master/README.md
Given a flight itinerary consisting of starting city, destination city, and ticket price (2d list) - find the optimal price flight path to get from start to destination. (A variation of Dynamic Programming Shortest Path)
public int minCost(List<String> lines, String source, String target, int k) {
if (lines.size() == 0 || k < 0) {
return 0;
}
Map<String, Flight> nodes = new HashMap<>();
for (String line : lines) {
String[] s = line.trim().split(",");
String[] t = s[0].trim().split("->");
String from = t[0];
String to = t[1];
int cost = Integer.valueOf(s[1].trim());
if (!nodes.containsKey(from)) nodes.put(from, new Flight(from));
if (!nodes.containsKey(to)) nodes.put(to, new Flight(to));
nodes.get(from).nextNodes.put(to, cost);
}
boolean find = false;
Queue<String> q = new LinkedList<>();
Queue<Integer> cost = new LinkedList<>();
q.offer(source);
cost.offer(0);
int stops = -1;
while (!q.isEmpty()) {
stops++;
if (stops > k + 1) {
break;
}
int qSize = q.size();
for (int i = 0; i < qSize; i++) {
Flight curr = nodes.get(q.poll());
int currCost = cost.poll();
curr.minCost = Math.min(curr.minCost, currCost);
if (curr.name.equals(target)) {
find = true;
continue;
}
for (String next : curr.nextNodes.keySet()) {
int nextCost = currCost + curr.nextNodes.get(next);
if (nextCost < nodes.get(next).minCost && (stops < k || stops == k && next.equals(target))) {
q.offer(next);
cost.offer(nextCost);
}
}
}
}
return find ? nodes.get(target).minCost : -1;
}
}
class Flight {
String name;
int minCost;
Map<String, Integer> nextNodes;
Flight(String name) {
this.name = name;
this.minCost = Integer.MAX_VALUE;
this.nextNodes = new HashMap<>();
}
}