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 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.

  1. Example 1:
  2. Input:
  3. n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
  4. src = 0, dst = 2, k = 1
  5. Output: 200
  6. Explanation:
  7. The graph looks like this:

  1. The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture.
  1. Example 2:
  2. Input:
  3. n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
  4. src = 0, dst = 2, k = 0
  5. Output: 500
  6. Explanation:
  7. The graph looks like this:

  1. 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.

这道题给了我们一些航班信息,包括出发地,目的地,和价格,然后又给了我们起始位置和终止位置,说是最多能转K次机,让我们求出最便宜的航班价格。那么实际上这道题就是一个有序图的遍历问题,博主最先尝试的递归解法由于没有做优化,TLE了,实际上我们可以通过剪枝处理,从而压线过OJ。首先我们要建立这个图,选取的数据结构就是邻接链表的形式,具体来说就是建立每个结点和其所有能到达的结点的集合之间的映射,然后就是用DFS来遍历这个图了,用变量cur表示当前遍历到的结点序号,还是当前剩余的转机次数K,访问过的结点集合visited,当前累计的价格out,已经全局的最便宜价格res。在递归函数中,首先判断如果当前cur为目标结点dst,那么结果res赋值为out,并直接返回。你可能会纳闷为啥不是取二者中较小值更新结果res,而是直接赋值呢?原因是我们之后做了剪枝处理,使得out一定会小于结果res。然后判断如果K小于0,说明超过转机次数了,直接返回。然后就是遍历当前结点cur能到达的所有结点了,对于遍历到的结点,首先判断如果当前结点已经访问过了,直接跳过。或者是当前价格out加上到达这个结点需要的价格之和大于结果res的话,那么直接跳过。这个剪枝能极大的提高效率,是压线过OJ的首要功臣。之后就是标记结点访问,调用递归函数,以及还原结点状态的常规操作了,参见代码如下:

解法一:

  1. class Solution {
  2. public:
  3. int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
  4. int res = INT_MAX;
  5. unordered_map<int, vector<vector<int>>> m;
  6. unordered_set<int> visited{{src}};
  7. for (auto flight : flights) {
  8. m[flight[]].push_back({flight[], flight[]});
  9. }
  10. helper(m, src, dst, K, visited, , res);
  11. return (res == INT_MAX) ? - : res;
  12. }
  13. void helper(unordered_map<int, vector<vector<int>>>& m, int cur, int dst, int K, unordered_set<int>& visited, int out, int& res) {
  14. if (cur == dst) {res = out; return;}
  15. if (K < ) return;
  16. for (auto a : m[cur]) {
  17. if (visited.count(a[]) || out + a[] > res) continue;
  18. visited.insert(a[]);
  19. helper(m, a[], dst, K - , visited, out + a[], res);
  20. visited.erase(a[]);
  21. }
  22. }
  23. };

下面这种解法是用BFS来做的,还是来遍历图,不过这次是一层一层的遍历,需要使用queue来辅助。前面建立图的数据结构的操作和之前相同,BFS的写法还是经典的写法,但需要注意的是这里也同样的做了剪枝优化,当当前价格加上新到达位置的价格之和大于结果res的话直接跳过。最后注意如果超过了转机次数就直接break,参见代码如下:

解法二:

  1. class Solution {
  2. public:
  3. int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
  4. int res = INT_MAX, cnt = ;
  5. unordered_map<int, vector<vector<int>>> m;
  6. queue<vector<int>> q{{{src, }}};
  7. for (auto flight : flights) {
  8. m[flight[]].push_back({flight[], flight[]});
  9. }
  10. while (!q.empty()) {
  11. for (int i = q.size(); i > ; --i) {
  12. auto t = q.front(); q.pop();
  13. if (t[] == dst) res = min(res, t[]);
  14. for (auto a : m[t[]]) {
  15. if (t[] + a[] > res) continue;
  16. q.push({a[], t[] + a[]});
  17. }
  18. }
  19. if (cnt++ > K) break;
  20. }
  21. return (res == INT_MAX) ? - : res;
  22. }
  23. };

再来看使用Bellman Ford算法的解法,关于此算法的detail可以上网搜帖子看看。核心思想还是用的动态规划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的价格之和,二者中取较小值更新即可,参见代码如下:

解法三:

  1. class Solution {
  2. public:
  3. int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
  4. vector<vector<int>> dp(K + , vector<int>(n, 1e9));
  5. dp[][src] = ;
  6. for (int i = ; i <= K + ; ++i) {
  7. dp[i][src] = ;
  8. for (auto x : flights) {
  9. dp[i][x[]] = min(dp[i][x[]], dp[i - ][x[]] + x[]);
  10. }
  11. }
  12. return (dp[K + ][dst] >= 1e9) ? - : dp[K + ][dst];
  13. }
  14. };

我们可以稍稍优化下上面解法的空间复杂度,使用一个一维的DP数组即可,具体思路没有啥太大的区别,参见代码如下:

解法四:

  1. class Solution {
  2. public:
  3. int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
  4. vector<int> dp(n, 1e9);
  5. dp[src] = ;
  6. for (int i = ; i <= K; ++i) {
  7. vector<int> t = dp;
  8. for (auto x : flights) {
  9. t[x[]] = min(t[x[]], dp[x[]] + x[]);
  10. }
  11. dp = t;
  12. }
  13. return (dp[dst] >= 1e9) ? - : dp[dst];
  14. }
  15. };

类似题目:

Maximum Vacation Days

参考资料:

https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/115596/c++-8-line-bellman-ford

https://leetcode.com/problems/cheapest-flights-within-k-stops/discuss/128217/Three-C++-solutions-BFS-DFS-and-BF

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Cheapest Flights Within K Stops K次转机内的最便宜的航班的更多相关文章

  1. [LeetCode] 787. Cheapest Flights Within K Stops K次转机内的最便宜航班

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

  2. LeetCode 787. Cheapest Flights Within K Stops

    原题链接在这里:https://leetcode.com/problems/cheapest-flights-within-k-stops/ 题目: There are n cities connec ...

  3. 【LeetCode】787. Cheapest Flights Within K Stops 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:DFS 方法二:BFS 参考资料 日期 题目 ...

  4. [Swift]LeetCode787. K 站中转内最便宜的航班 | 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 pri ...

  5. [LeetCode] 787. Cheapest Flights Within K Stops_Medium tag: Dynamic Programming, BFS, Heap

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

  6. 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 pri ...

  7. 【力扣leetcode】-787. K站中转内最便宜的航班

    题目描述: 有 n 个城市通过一些航班连接.给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 p ...

  8. Leetcode 703. 数据流中的第K大元素

    1.题目要求 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器, ...

  9. LeetCode:数组中的第K个最大元素【215】

    LeetCode:数组中的第K个最大元素[215] 题目描述 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: ...

随机推荐

  1. 轴对称 Navier-Stokes 方程组的一个点态正则性准则

    对轴对称 NSE, 我们改进了 [Pan, Xinghong. A regularity condition of 3d axisymmetric Navier-Stokes equations. A ...

  2. [物理学与PDEs]第5章第6节 弹性静力学方程组的定解问题

    5. 6 弹性静力学方程组的定解问题 5. 6. 1 线性弹性静力学方程组 1.  线性弹性静力学方程组 $$\bee\label{5_6_1_le} -\sum_{j,k,l}a_{ijkl}\cf ...

  3. docker学习------docker私有仓库的搭建

    192.168.138.102:23451.私有仓库的搭建(docker pull registry),拉取最新的镜像 2.查看拉取的仓库镜像(docker images) 3.启用registry镜 ...

  4. 第六章 接口,lamda表达式与内部类

    接口 接口可以包含常量, 且不需要publish static final修饰, 接口的域会自动添加该修饰符. Java建议不要写多余的代码,因此省略修饰符更简洁. 全部都是常量的接口背离了接口的初衷 ...

  5. Android WebView重定向问题的解决方案

    当WebView在加载网页时,有时会有重定向问题,返回键回退怎么也回退不掉,怎么办? WebView有一个getHitTestResult():返回的是一个HitTestResult,一般会根据打开的 ...

  6. 将代码上传版本库gitee

    首先在电脑中安装git,配置好环境变量. 在后台输入命令上传 上传账号的用户名git config --global user.name "" 上传账号的邮箱git config ...

  7. Dos.Common

    引言: Dos.Common是一个开发中的常用类库,如HttpHelper.LogHelper.CacheHelper.CookieHelper.MapperHelper等等.与Dos.WeChat. ...

  8. Lua中字符串库中的几个重点函数

    [前言] 在<Lua中的一些库(1)>中也说到了,要对string库的模式匹配进行单独的讲解.对于字符串的处理,对于任何语言的学习来说,都是一个难点,而且也是一个必会的知识点.给你一个字符 ...

  9. Git常用的操作

    1.git使用的常规操作     git pull——>编辑——>git add——>git commit——>git push 用git add把文件添加进去,实际上就是把文 ...

  10. 【原创】大数据基础之Oozie(2)使用

    命令行 $ oozie help 1 导出环境变量 $ export OOZIE_URL=http://oozie_server:11000/oozie 否则都需要增加 -oozie 参数,比如 $ ...