2008-出租车的最大盈利

Raphael Liu Lv10

你驾驶出租车行驶在一条有 n 个地点的路上。这 n 个地点从近到远编号为 1n ,你想要从 1 开到 n
,通过接乘客订单盈利。你只能沿着编号递增的方向前进,不能改变方向。

乘客信息用一个下标从 0 开始的二维数组 rides 表示,其中 rides[i] = [starti, endi, tipi] 表示第
i 位乘客需要从地点 starti 前往 endi ,愿意支付 tipi 元的小费。

每一位 你选择接单的乘客 i ,你可以 盈利 endi - starti + tipi 元。你同时 最多
只能接一个订单。

给你 nrides ,请你返回在最优接单方案下,你能盈利 最多 多少元。

注意: 你可以在一个地点放下一位乘客,并在同一个地点接上另一位乘客。

示例 1:

**输入:** n = 5, rides = [ _ **[2,5,4]**_ ,[1,5,1]]
**输出:** 7
**解释:** 我们可以接乘客 0 的订单,获得 5 - 2 + 4 = 7 元。

示例 2:

**输入:** n = 20, rides = [[1,6,1], ** _[3,10,2]_** , _ **[10,12,3]**_ ,[11,12,2],[12,15,2], ** _[13,18,1]_** ]
**输出:** 20
**解释:** 我们可以接以下乘客的订单:
- 将乘客 1 从地点 3 送往地点 10 ,获得 10 - 3 + 2 = 9 元。
- 将乘客 2 从地点 10 送往地点 12 ,获得 12 - 10 + 3 = 5 元。
- 将乘客 5 从地点 13 送往地点 18 ,获得 18 - 13 + 1 = 6 元。
我们总共获得 9 + 5 + 6 = 20 元。

提示:

  • 1 <= n <= 105
  • 1 <= rides.length <= 3 * 104
  • rides[i].length == 3
  • 1 <= starti < endi <= n
  • 1 <= tipi <= 105

Problem: 2830. 销售利润最大化 【几乎一样2008】

Problem: 435. 无重叠区间 【权重一样】

Problem: 1751. 最多可以参加的会议数目 II 【区间个数也有限制】

Problem: 2054. 两个最好的不重叠活动 【同1751】

[TOC]

思路

主要思路就是想清楚以end结尾的这个日程选还是不选,选的话代价是从start的时刻之前的总和加上当前的权值,不选的话就是等于前一个时刻的权值。

这类题目分为两种类型
一种是密集得到每一个时间的值,时间复杂度O(n),另一种是二分得到每一个结束时间的值,时间复杂度O(nlogn)。
还有个小细节是 看选择的区间端点是否允许重合,如果不允许的话那么你选了end结尾的权重后,要用 dp[start-1] + 当前时刻得到的权值,如果允许的话,那就是 dp[start] + 当前时刻得到的权值。

解题方法

如果时间跨度较大,是10**9次方,那么就是线性dp+二分,否则是线性dp

2008 Code

[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def maxTaxiEarnings(self, n: int, rides: List[List[int]]) -> int:
# 按照终点进行分组
groups = [[] for _ in range(n+1)]
for s,e,t in rides:
groups[e].append((s,e-s+t))
dp = [0 for _ in range(n+2)]
for end, group in enumerate(groups):
dp[end+1] = dp[end]
if group:
for s, t in group:
dp[end+1] = max(dp[end+1],dp[s+1] + t)
# print(dp)
return dp[n+1]

2830 Code

[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def maximizeTheProfit(self, n: int, offers: List[List[int]]) -> int:
dp = [0] * (n+1)
# dp[3] = dp[1] + 2
# dp[1] = dp[0]
# 按照最后一个节点的位置分组
groups = [[] for _ in range(n)]
for s,e,g in offers:
groups[e].append((s,g))
for end,x in enumerate(groups):
dp[end+1] = dp[end] # 如果不买
if x:
for s,g in x:
dp[end+1] = max(dp[s] + g,dp[end+1])
return dp[n]

1235 Code

[]
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int:
jobs = sorted(zip(startTime,endTime,profit),key = lambda x:x[1])
dp = (len(jobs) + 1) * [0]
for i, (s,e,p) in enumerate(jobs):
# 这句话的意思是说从jobs[0:i]里面找到一个end值>当前i的start的索引
# -1就是变成了找到一个小于等于当前时间s开始的最近的jobs的工作
j = bisect_right(jobs, s, hi = i, key = lambda x:x[1])-1
# print(dp[j+1]+p)
dp[i+1] = max(dp[i],dp[j+1] + p)
# print(dp)
return dp[-1]

435 Code

[]
1
2
3
4
5
6
7
8
9
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
n = len(intervals)
intervals.sort(key = lambda x:x[1])
dp = [0] * (n+1)
for i,(s,e) in enumerate(intervals):
j = bisect_right(intervals,s,hi=i,key=lambda x:x[1])-1
dp[i+1] = max(dp[i],dp[j+1]+1)
return n-dp[-1]

1751 Code

[]
1
2
3
4
5
6
7
8
9
10
class Solution:
def maxValue(self, events: List[List[int]], k: int) -> int:
events.sort(key=lambda e: e[1])
n = len(events)
f = [[0] * (k + 1) for _ in range(n + 1)]
for i, (start, end, val) in enumerate(events):
p = bisect_left(events, start, hi=i, key=lambda e: e[1])-1 # hi=i 表示二分上界为 i(默认为 n)
for j in range(1, k + 1):
f[i + 1][j] = max(f[i][j], f[p+1][j - 1] + val)
return f[n][k]

2054 Code

[]
1
2
3
4
5
6
7
8
9
10
class Solution:
def maxTwoEvents(self, events: List[List[int]]) -> int:
events.sort(key = lambda x:x[1])
n = len(events)
dp = [[0] * (2+1) for _ in range(n+1)]
for i, (s,e,v) in enumerate(events):
j = bisect_left(events,s,hi = i,key = lambda x:x[1])-1
for k in range(1,2+1):
dp[i+1][k] = max(dp[i][k],dp[j+1][k-1] + v)
return dp[-1][-1]
 Comments