现有N
个任务需要在T
时间内处理完成,同一时间只能处理一个任务,处理每个任务所需要的时间固定为1
。
每个任务都有最晚处理时间限制和报酬,在最晚处理时间点之前处理完成任务才可获得对应的报酬奖励。
可用于处理任务的时间有限,请问在有限的时间内,可获得的最多报酬?
1 < N < 100`,`1 < T < 100
第一行输入两个数T
和N
,表示N
个任务和全部任务的最迟的时间节点T
。
接下来输入N
行,每一行输入两个数K
和L
表示一个任务,K
为这个任务的最晚完成时间,L
为完成该任务能够获得的报酬。
一个整数,表示能够获取的最大报酬。
3 4
1 2
1 3
1 4
2 5
9
在单位时间1
,完成任务2
,获得报酬4
在单位时间2
,完成任务3
,获得报酬5
3 5
1 3
2 2
3 1
3 4
4 5
12
在单位时间1
,完成任务0
,(最晚完成时间是1
),获得报酬3
在单位时间2
,完成任务4
,(最晚完成时间是4
),获得报酬5
在单位时间3
,完成任务3
,(最晚完成时间是3
),获得报酬4
2 3
1 2
2 5
1 5
10
本题的陷阱在于,每一个任务给定的时间K
是最晚完成时间,这意味该任务可以在小于等于K
的任意一个时刻完成。
譬如对于示例二
1
,第0-4
个任务都是可以选择去完成的任务。2
,第1-4
个任务都是可以选择去完成的任务。3
,第2-4
个任务都是可以选择去完成的任务。如果意识到了这一点,那么以下结论是显而易见的:随着时间增大,某些任务已经错过了其对应的最晚完成时间K
,那么可以选择的任务是变得越来越少的。
难点在于,在当前时间较小的时候,如果我们面临多个选择,我们无法确定应该选择哪个任务。因为我们在做选择时存在两个不同的维度需要考虑:
L
尽可能地大K
尽可能地小如果正向地考虑时间变化,我们没有办法同时保持上述两个维度都满足最优的条件。换句话说,没有办法贪心地从局部最优得到全局最优解。
考虑一种特殊情况,假设仅存在一个任务i
的最晚完成时间K
大于等于总体最晚完成时间T
,那么在时刻T
时,只有这一个任务i
可以被选择。即使这个任务i
有可能可以在更早完成,我们也希望把它拖到时刻T
来完成,这样才能让时刻T
之前的单位时间去完成其他任务。
以示例三为例,对于时刻t = 2
,仅存在工作1
的最晚完成时间是K = 2
,那么工作1
我们一定希望把它放在t = 2
来完成而不是放在t = 1
来完成,因为这样才能在t = 1
时刻,去完成更多任务。
所以贪心策略应该是从后往前考虑时间变化。即时刻t
从T
递减变化到1
。
t
,假设该时刻有m
个可以选择的任务,那么我们会在里面挑出报酬最大的那个任务去完成。t-1
,除了剩下的m-1
个任务,还有最晚完成时间K = t-1
的若干个任务(假设数量为p
)需要放在一起考虑,那么此时一共有p+m-1
个任务需要考虑,同样在里面挑出报酬最大的那个任务去完成。for t in range(T, 0, -1):
cur_task_lst += dic_task_last_t[t]
cur_task_lst.sort()
if len(cur_task_lst) > 0:
ans += cur_task_lst.pop()
其中dic_task_last_t
为一个哈希表,储存了最晚完成时间为时刻t
的任务的报酬。其
key
为时刻t
value
为由最晚完成时间为t
的任务的报酬所构成的列表注意:上述挑选单个时刻里最大值的过程,更好的方法是用一个最大容量为
T
的优先队列/堆来维护,这样单个时刻挑选最大值的复杂度可以降为O(logT)
。但因为数据量很小,所以直接排序也是可以通过的。感兴趣的同学可以尝试用优先队列的方法来维护上述过程。
# 题目:【贪心】2023C-在规定时间内获得的最大报酬
# 分值:100
# 作者:闭着眼睛学数理化
# 算法:贪心
# 代码看不懂的地方,请直接在群上提问
from collections import defaultdict
# 用一个哈希表储存最晚完成时间为时刻t的任务的报酬
# key为时刻t
# value为由最晚完成时间为t的任务的报酬所构成的列表
dic_task_last_t = defaultdict(list)
# 输入总体最晚完成时间T,任务个数N
T, N = map(int, input().split())
# 循环N次,输入每一行
for _ in range(N):
# 输入最晚完成时间K,完成该任务获得的报酬L
K, L = map(int, input().split())
# 如果K大于T,那么该任务最晚是在时刻T完成
# 如果K小于T,那么该任务最晚是在时刻K完成
# 因此键需要取两者之间的较小值
dic_task_last_t[min(K, T)].append(L)
ans = 0
# 在时刻t时,可以选择的任务的报酬构成的列表,一开始为空列表
cur_task_lst = list()
# 逆向遍历从T到1的所有时刻t
for t in range(T, 0, -1):
# t时刻可以完成的任务的报酬储存在dic_task_last_t[t]中
# 将其更新入cur_tack_lst中
cur_task_lst += dic_task_last_t[t]
# 对cur_task_lst进行排序
cur_task_lst.sort()
# 如果此时cur_task_lst不为空,则选择其中报酬最大的任务去完成
# 即删除cur_task_lst末尾元素,并将该元素更新入ans中
if len(cur_task_lst) > 0:
ans += cur_task_lst.pop()
print(ans)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int T = scanner.nextInt(); // 总体最晚完成时间
int N = scanner.nextInt(); // 任务个数
Map<Integer, List<Integer>> dic_task_last_t = new HashMap<>();
for (int i = 0; i < N; i++) {
int K = scanner.nextInt(); // 最晚完成时间
int L = scanner.nextInt(); // 任务报酬
// 存储最晚完成时间为t的任务的报酬列表
int t = Math.min(K, T);
dic_task_last_t.computeIfAbsent(t, k -> new ArrayList<>()).add(L);
}
int ans = 0;
List<Integer> curTaskList = new ArrayList<>();
for (int t = T; t > 0; t--) {
List<Integer> taskList = dic_task_last_t.getOrDefault(t, new ArrayList<>());
curTaskList.addAll(taskList);
Collections.sort(curTaskList);
if (!curTaskList.isEmpty()) {
int maxReward = curTaskList.remove(curTaskList.size() - 1);
ans += maxReward;
}
}
System.out.println(ans);
}
}
#include
#include
#include
#include
using namespace std;
int main() {
int T, N;
cin >> T >> N;
unordered_map<int, vector<int>> dic_task_last_t;
for (int i = 0; i < N; i++) {
int K, L;
cin >> K >> L;
int t = min(K, T);
dic_task_last_t[t].push_back(L);
}
int ans = 0;
vector<int> curTaskList;
for (int t = T; t > 0; t--) {
vector<int> taskList = dic_task_last_t[t];
curTaskList.insert(curTaskList.end(), taskList.begin(), taskList.end());
sort(curTaskList.begin(), curTaskList.end());
if (!curTaskList.empty()) {
int maxReward = curTaskList.back();
curTaskList.pop_back();
ans += maxReward;
}
}
cout << ans << endl;
return 0;
}
时间复杂度:O(TNlogN)
。一共需要考虑T
个时刻。考虑每一个时刻时,都要对列表cur_task_lst
进行排序,cur_task_lst
的最大值为N
,单次排序的复杂度为O(NlogN)
。
空间复杂度:O(N)
。哈希表所占空间。
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
可查看链接 大厂真题汇总 & OD真题汇总(持续更新)
绿色聊天软件戳 od1336
了解更多