题目来源
题目描述
class Solution {
public:
int scheduleCourse(vector<vector<int>>& courses) {
}
};
题目解析
分析
- 假如给定了两门截止时间不同的课程,我们应该如何学习呢?
- 带入到现实中,我们肯定先学习截止时间更近的课程,比如,一门课程在10天后截止,另一门在100天后截止,你肯定会先学习10天后截止的那门课程,对不对?
- 比如说,给定的两门课程为:[1, 3] 和 [2, 5],这时候,我们无论先学习哪门课程都是可以完成两门课程的。
- 再比如说,给定的两门课程为:[1, 2] 和 [2, 4],这时候,如果先学习后者再学习前者,会导致 d2 + d1 > l1,将无法完成前者的学习,因此,我们只能先学习前者再学习后者。
- 因此,我们可以先将所有课程按照截止时间排序,然后依次学习
- 但是,这样并不能保证我们前面遍历到的课程学习了,后面的课程就一定能够学习。
- 比如说,给定了三门课程分别为:[1,2] 、[3, 4]、[2, 5],这时候,当选择到第三门课程的时候,发现 1 + 3 + 2 > 5 的,所以,第三门课程是学习不了的,那么,这时候,在第二门和第三门课程中间做一个选择,你会怎么选择呢?(因为后面可能还有个 [3, 6] 的课程)
- 因为题目中要求了返回能够学习的最大数目,所以我们应该优先学习时长更短的课程。对于上例来说,我们选择 [2, 5] 优于 [3, 4],因为 [2, 5] 学习时长更短,相当于我们可以有更多的时间学习后续的课程。
- 因此,我们可以使用优先队列来维护已经选择了的课程,当出现冲突时,我们比较堆顶元素与待选择元素的学习时长,选择时长更短的课程学习
算法:
- 先按照截止时间升序排序
- 然后依次遍历一个课程(用优先队列维护已选课程),当遍历到
(
t
i
,
d
i
)
(t_i, d_i)
(ti,di)时:
- 如果当前优先队列中所有课程的总时间与 t i t_i ti之和小于等于 d i d_i di,那么就将 t i t_i ti放入优先队列中
- 如果当前优先遍历中所有课程的总时间与 t i t_i ti之和大于 d i d_i di,那么我们找到优先队列中的最大元素 t m t_m tm,如果 t m > t i t_m > t_i tm>ti,替换掉
- 最终优先队列的长度就是我们能最多修习的课程
class Solution {
public:
int scheduleCourse(vector<vector<int>>& courses) {
sort(courses.begin(), courses.end(), [](const auto &a, const auto &b){
return a[1] < b[1];
});
std::priority_queue<int> q;
int total = 0; // 优先队列中所有课程的总时间
for(auto c : courses){
int ti = c[0], di = c[1];
if(total + ti <= di){
total += ti;
q.push(ti);
}else if(!q.empty() && q.top() > ti){
total -= q.top() - ti;
q.pop();
q.push(ti);
}
}
return q.size();
}
};