|
| 1 | +## 题目地址(2008. 出租车的最大盈利) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/maximum-earnings-from-taxi/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +你驾驶出租车行驶在一条有 n 个地点的路上。这 n 个地点从近到远编号为 1 到 n ,你想要从 1 开到 n ,通过接乘客订单盈利。你只能沿着编号递增的方向前进,不能改变方向。 |
| 9 | +
|
| 10 | +乘客信息用一个下标从 0 开始的二维数组 rides 表示,其中 rides[i] = [starti, endi, tipi] 表示第 i 位乘客需要从地点 starti 前往 endi ,愿意支付 tipi 元的小费。 |
| 11 | +
|
| 12 | +每一位 你选择接单的乘客 i ,你可以 盈利 endi - starti + tipi 元。你同时 最多 只能接一个订单。 |
| 13 | +
|
| 14 | +给你 n 和 rides ,请你返回在最优接单方案下,你能盈利 最多 多少元。 |
| 15 | +
|
| 16 | +注意:你可以在一个地点放下一位乘客,并在同一个地点接上另一位乘客。 |
| 17 | +
|
| 18 | + |
| 19 | +
|
| 20 | +示例 1: |
| 21 | +
|
| 22 | +输入:n = 5, rides = [[2,5,4],[1,5,1]] |
| 23 | +输出:7 |
| 24 | +解释:我们可以接乘客 0 的订单,获得 5 - 2 + 4 = 7 元。 |
| 25 | +
|
| 26 | +
|
| 27 | +示例 2: |
| 28 | +
|
| 29 | +输入:n = 20, rides = [[1,6,1],[3,10,2],[10,12,3],[11,12,2],[12,15,2],[13,18,1]] |
| 30 | +输出:20 |
| 31 | +解释:我们可以接以下乘客的订单: |
| 32 | +- 将乘客 1 从地点 3 送往地点 10 ,获得 10 - 3 + 2 = 9 元。 |
| 33 | +- 将乘客 2 从地点 10 送往地点 12 ,获得 12 - 10 + 3 = 5 元。 |
| 34 | +- 将乘客 5 从地点 13 送往地点 18 ,获得 18 - 13 + 1 = 6 元。 |
| 35 | +我们总共获得 9 + 5 + 6 = 20 元。 |
| 36 | +
|
| 37 | + |
| 38 | +
|
| 39 | +提示: |
| 40 | +
|
| 41 | +1 <= n <= 10^5 |
| 42 | +1 <= rides.length <= 3 * 104 |
| 43 | +rides[i].length == 3 |
| 44 | +1 <= starti < endi <= n |
| 45 | +1 <= tipi <= 105 |
| 46 | +``` |
| 47 | + |
| 48 | +## 前置知识 |
| 49 | + |
| 50 | +- 动态规划 |
| 51 | +- 二分 |
| 52 | + |
| 53 | +## 公司 |
| 54 | + |
| 55 | +- 暂无 |
| 56 | + |
| 57 | +## 思路 |
| 58 | + |
| 59 | +这是一个典型的最长上升子序列(LIS)问题。如果你对最长上升子序列不熟悉,强烈建议先看一下我之前写的专题:https://lucifer.ren/blog/2020/06/20/LIS/ |
| 60 | + |
| 61 | +LIS 问题的常规做法是 $n^2$ , 而这道题的数据范围是 $10^5$, 这意味着我们使用平方的解法是无法通过的。关于这点不明白的可以看下我之前写的文章:https://lucifer.ren/blog/2020/12/21/shuati-silu3/ |
| 62 | + |
| 63 | +我们可以用动态规划来解, dp[i] 表示仅考虑 rides 0 到 i (左右闭合区间),所能挣的最多的钱。因此 dp[len(rides)-1] 就是答案。 |
| 64 | + |
| 65 | +那么状态如何转移呢?传统的 LIS 问题,对于每一个 j 我们都向前找到第一个满足 rides[j][0] >= rides[i][1] 的 i,我们需要两层循环枚举所有可能。那么如何优化呢? |
| 66 | + |
| 67 | +实际上前面的文章也提到过,这里再次强调一下。由于我们需要**向前找到第一个满足 rides[j][0] >= rides[i][1] 的 i**,那么我们可以先对结束时间排序,接下来二分找到**最右满足条件的 i**,这样时间复杂度可以降低到 $nlogn$。 由于这是一个典型的最右满足条件的二分,我们直接使用模板解决。不熟悉二分的可以看下我的二分专题:https://lucifer.ren/blog/2021/03/08/binary-search-1/ |
| 68 | + |
| 69 | +> 由于我们是找满足条件的 dp[i][1] ,因此需要对结束时间而不是开始时间排序 |
| 70 | +
|
| 71 | +## 关键点 |
| 72 | + |
| 73 | +- 二分优化时间复杂度 |
| 74 | + |
| 75 | +## 代码 |
| 76 | + |
| 77 | +- 语言支持:Python3 |
| 78 | + |
| 79 | +Python3 Code: |
| 80 | + |
| 81 | +```python |
| 82 | + |
| 83 | +class Solution: |
| 84 | + def maxTaxiEarnings(self, n: int, rides: List[List[int]]) -> int: |
| 85 | + rides.sort(key=lambda x:x[1]) |
| 86 | + |
| 87 | + n = len(rides) |
| 88 | + dp = [e-s+t for s,e,t in rides] |
| 89 | + def bisect_right(rides, i): |
| 90 | + l, r = 0, i |
| 91 | + while l <= r: |
| 92 | + mid = (l+r)//2 |
| 93 | + if rides[i][0] >= rides[mid][1]: |
| 94 | + l = mid + 1 |
| 95 | + else: |
| 96 | + r = mid - 1 |
| 97 | + return r |
| 98 | + for j in range(1, n): |
| 99 | + i = bisect_right(rides, j) |
| 100 | + if i == -1: |
| 101 | + dp[j] = max(dp[j], dp[j-1]) |
| 102 | + else: |
| 103 | + dp[j] = max(dp[j], dp[j-1], dp[i] + rides[j][1] - rides[j][0] + rides[j][2]) |
| 104 | + return max(dp) |
| 105 | + |
| 106 | +``` |
| 107 | + |
| 108 | +**复杂度分析** |
| 109 | + |
| 110 | +令 n 为数组长度。 |
| 111 | + |
| 112 | +- 时间复杂度:$O(nlogn)$ |
| 113 | +- 空间复杂度:$O(n)$ |
| 114 | + |
| 115 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 116 | +
|
| 117 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 118 | + |
| 119 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 120 | + |
| 121 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 122 | + |
| 123 | + |
0 commit comments