题目
有N件兼职,这些兼职都是为期一天的兼职。在这N件兼职中,如果选择第i份兼职的话,会在 Ai天后,拿到报酬Bi
你能够在暑假的某一天选择一份兼职。但是同一份兼职不能在暑假的不同的日子里被重复选择
从今天开始到M天以后(这里包含第M+1天),你能够得到的最大的报酬是多少
你可以从今天开始选择做兼职
条件
- 输入的数都为整数
- 1 <= N <= 1000000
- 1 <= M <= 1000000
- 1 <= Ai <= 1000000
- 1 <= Bi <= 1000000
输入
要求以下面的方式输入
N M
A1 B1
A2 B2
A3 B3
.
.
.
An-1 Bn-1
输出
输出所能获取的最大报酬
例1
输入
3 4
4 3
4 1
2 2
输出
5
解释
通过一下的方式选择兼职,可以多的最大的报酬
一共有3份兼职,最后一天是今天起第5天
- 第1天,选择(4 3),就是4天后(第5天)获得3报酬的兼职
- 第2天,选择(2 2),就是2天后,也是明天起第2天,今天起第3天,获得2报酬的兼职
- 第3天,没得选了,因为剩下的(4,1)是后天起第4天,今天起第6天,超过了最后一天是今天起第5天
所以总共是3+2=5,5个报酬
例2
输入
5 3
1 2
1 3
1 4
2 1
2 3
输出
10
解释
通过一下的方式选择兼职,可以多的最大的报酬
一共有5份兼职,最后一天是今天起第4天
- 第1天,选择(2 3),就是2天后(第3天)获得3报酬的兼职
- 第2天,选择(1 3), 就是1天后,也是明天起第2天,今天起第3天,获得3报酬的兼职
- 第2天,选择(1 4), 就是1天后,也是后天起第1天,今天起第4天,获得4报酬的兼职
所以总共是3+3+4=10,10个报酬
解题思路
M的含义
- 从第1天到第M天是可以打工的
- 从第2天到第M+1天是可以领工资的
比如说M=4
最后能拿工资的是第5天
最后能够打工的是第4天
从后面的天开始选起
- 天数越往后,离最后一天就越近,剩下的留给等发工资的天数就越少
- 天数越往前,离最后一天就越远,剩下的留给等发工资的天数就越多
然后我们手上有一堆打工的清单
比如说
M=4
3天后拿5块钱
5天后拿3块钱
2天后拿1块钱
2天后拿3块钱
1天后拿1块钱
那么对于能打工的第4天来说,能选择的只能是一天后拿到1块钱的兼职
符合后些天时间限制的兼职,肯定也满足前些天的时间限制
我们尝试在选择每一天的最大的报酬的时候,先把所有满足时间限制的兼职加入到候补中,再取候补中最大的值
这个候补区是所有天都公用的,因为是从后些天开始累加的
步骤
1.从最后一天开始,因为是最后一天,所以只能选一天后给报酬的,然后有一个选项进入候补,弹出唯一的,这个时候候补区就变成了空的,然后把空的候补给上一天
2.再进入到第三天,有两份兼职满足条件进入候补,选择报酬最大的弹出,这个时候,候补区剩下一份兼职,然后把剩下一份兼职的候补区给上一天
3.再进入到第二天,有一份新的兼职满足要求,加入候补,然后弹出最大报酬的兼职,然后把候补区给上一天
4.再进入到第一天,发现没有新的兼职满足要求,但是候补里还有兼职,弹出唯一的一份兼职
代码
from heapq import heappop, heappush
def calculate(N,M,arr):
ret = 0
j = 0
res = []
# 1,2,3,4,5
# ↑
# 4,3,2,1
# M = 4
# range(1,5) => 1,2,3,4
for i in range(1,M+1):
# i = 1,2,3
# 比如M=4的话,第三天(最多1天),第二天(最多2天),第一天(最多3天)
# 记下来循环打工信息资源,以j为标记
# j为资源编号
# arr[j][0]为该编号资源的天数
# 候补的资源,满足所有天数,因为是满足后续的天数来算的
while (j < N) and (arr[j][0] <= i):
heappush(res, -arr[j][1]) #headpush 会把最小的顶在前面,也就是说把最大的arr[j][1]也即是报酬顶在最前面
j = j + 1 #j找到的话就往上顶一格,一共这一天,或者前一天寻找的时候以下一个资源天为开始
if len(res) > 0:
ret -= heappop(res) #headpop抛出最前面的值,也就是最下的负数,最大的报酬
return ret
N,M = map(int,input().split())
# 后面的逻辑是基于前面的从小到大的排序的
list = [list(map(int,input().split())) for i in range(N)]
arr = sorted(list,key=lambda x:x[0])
result = calculate(N,M,arr)
print(result)
总结
最总要的是要理解这两点
- 从后往前,每走一天,积累候补,然后没走完一天,弹出报酬最大候补
- 候补是公用
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。