[Leetcode] 787 Cheapest Flights Within K Stops

Posted goldenticket

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Leetcode] 787 Cheapest Flights Within K Stops相关的知识,希望对你有一定的参考价值。

原题链接

There are n cities connected by m flights. Each fight starts from city and arrives at v with a price w.

Now given all the cities and flights, 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 city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture.
Example 2:
Input: 
n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
src = 0, dst = 2, k = 0
Output: 500
Explanation: 
The graph looks like this:

技术图片

The cheapest price from city 0 to city 2 with at most 0 stop costs 500, as marked blue in the picture.

Note:

  • The number of nodes n will be in range [1, 100], with nodes labeled from 0 to n - 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.

题目理解:这道题给的是一个direct weight graph,中间可能有环。给定一个起点和一个终点,求在k个vertices中经过的path weight和最少是多少。

这道题有几个关键的点需要注意:

1. 可能有环:需要一个unordered_set来记录所有已经访问过的vertices

2. 一个起点一个终点:是一个典型的search问题,那么有三种思路:DFS, BFS, DP

3. 返回的是一个最小的weight,而不是path,所以一定可以优化成一个DP的问题

那么现在就从最简单的BFS的方法开始:

解法1:

使用BFS的话首先要做图,从CLRS上可以看到一共有两种制图方法:

Because the adjacency-list representation provides a compact way to represent sparse graphs —those for which |E| is much

less than |V|2 —it is usually the method of choice. Most of the graph algorithms presented in this book assume that an input graph

is represented in adjacency-list form. We may prefer an adjacency-matrix representation, however, when the graph is dense—|E| is

close to |V|2 —or when we need to be able to tell quickly if there is an edge connecting two given vertices.

 

翻译一下就是从相邻表和矩阵表中选择,矩阵表在这道题中不好使用,因为如果我们需要寻找下一个机场的位置那么一定要访问当前节点的

所有相邻机场,所以我们在这里选择使用相邻表。未来如果有机会可以深入讨论如何使用矩阵表。相邻表最常用的写法是用一个unordered_map

的数据结构,key -> 当前节点,value -> 所有相邻的节点。这道题还有一个特殊的地方,就是edge是有weight,那么在存储相邻节点的时候还要

再存当前edge的weight。那么现在graph就变成:unordered_map<int, vector<vector<int>> (其实也可以用pair)

 

遍历的时候每次的weight便是从从src到上一个节点的总weight+从上一个节点到目前节点的weight,也就是18行的意思。在中间有一次剪枝:如果

当前的weight已经比之前算过的,可以达到dst的weight要高,那么就prune掉这个节点,因为未来无论如何都不会比当前的weight高。最后不要忘记我们

还有一个条件没有用:最多可以有k个节点,我们可以用一个cnt数字代表当前的节点数,因为我们并不需要知道具体的path是什么。这个解法的最坏时间复杂度是

当k=# of vertices,同时每一个edge的weight都很接近(很难prune),每个机场都相连,这个时候总复杂度是O(n+# of edge)

 1 class Solution {
 2     public:
 3     int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
 4         int res = 1e9, cnt = 0;
 5         unordered_map<int, vector<vector<int>>> m;
 6         queue<vector<int>> q{{{src, 0}}};
 7         for (auto flight : flights) {
 8             m[flight[0]].push_back({flight[1], flight[2]}); // key: src,  value: {dst, weight}
 9         }
10         while (!q.empty()) {
11             for (int i = q.size(); i > 0; --i) {
12                 auto t = q.front(); 
13                 q.pop();
14                 if (t[0] == dst) res = min(res, t[1]); // now you reach the dst, you can check the weight
15                 for (auto a : m[t[0]]) {
16                     if (t[1] + a[1] > res) // skip if the cost is already more than the previous minimal cost
17                         continue;
18                     q.push({a[0], t[1] + a[1]}); // we now have a new path from old src to the neighour
19                 }
20             }
21             if (cnt++ > K) // now we are going to expand to next level
22                 break;
23         }
24         return (res == 1e9) ? -1 : res;
25     }
26 };

 

解法2:

使用DP的思想首先要找到状态表示方程,如果在任意一个vertice,那么我们有三个状态:

1. 当前的vertice

2. 当前的总weight (从src)

3. 当前经过的所有节点数

我们要求的是最小的weight,所以我们把dp设置成dp[节点数][当前节点]=最小weight(注意,这里是最小,否则没有意义)

那么如果想要知道从src到dst的总最小cost,用dp方程表示便是dp[k][dst]。如何从上一个状态来获取现在的最小值呢?

我们先看上一个状态有哪些可能:假设当前的节点数是i, 当前的节点是j

1. dp[i - 1][all the previous neighbour] + the weight from previous neighbour to j

2. dp[i][j] (just skip last neighbour, and fly directly from src to j)

为了得到所有的neighbour,需要在做一个关于当前neighbour的loop,所以一共有两个loop

这样做的的时间复杂度实际上也是O(k * n)

 1 #define vvi vector<vector<int>>
 2 #define vi vector<int>
 3 
 4 class Solution {
 5 public:
 6     int findCheapestPrice(int n, vvi& flights, int src, int dst, int K) {
 7         vvi dp(K + 2, vi(n, 1e9)); // all init as unreachable
 8         dp[0][src] = 0;
 9         for (int i = 1; i < K + 2; ++i) {
10             dp[i][src] = 0; // src to src with whatever stops doesn‘t cost anything
11             for (vi f : flights) // src, dst, weight
12                 dp[i][f[1]] = min(dp[i][f[1]], dp[i - 1][f[0]] + f[2]);
13         }
14         return dp[K + 1][dst] == 1e9 ? -1 : dp[K + 1][dst];
15     }
16 };

 

以上是关于[Leetcode] 787 Cheapest Flights Within K Stops的主要内容,如果未能解决你的问题,请参考以下文章

Dijkstra’s Shortest Path Algorithm / LeetCode 787. Cheapest Flights Within K Stops

787. Cheapest Flights Within K Stops

LWC 72: 787. Cheapest Flights Within K Stops

787. Cheapest Flights Within K Stops

787. Cheapest Flights Within K Stops

787. Cheapest Flights Within K Stops