LeetCode:630.课程表III

97 阅读2分钟

630.课程表III

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/co…

这里有 n 门不同的在线课程,按从 1 到 n 编号。给你一个数组 courses ,其中 courses[i]=[durationi,lastDayi]courses[i] = [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。

你的学期从第 1 天开始。且不能同时修读两门及两门以上的课程。

返回你最多可以修读的课程数目。

示例 1: 输入:courses = [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]] 输出:3 解释: 这里一共有 4 门课程,但是你最多可以修 3 门: 首先,修第 1 门课,耗费 100 天,在第 100 天完成,在第 101 天开始下门课。 第二,修第 3 门课,耗费 1000 天,在第 1100 天完成,在第 1101 天开始下门课程。 第三,修第 2 门课,耗时 200 天,在第 1300 天完成。 第 4 门课现在不能修,因为将会在第 3300 天完成它,这已经超出了关闭日期。

示例 2:

输入:courses = [[1,2]] 输出:1

示例 3:

输入:courses = [[3,2],[4,3]] 输出:0

提示:

1 <= courses.length <= 104 1 <= durationi, lastDayi <= 104

解法

  • 贪心算法+堆
  • 需要一个数据结构支持「取出 tt 值最大的那门课程」,因此我们可以使用优先队列(大根堆)。依次遍历每一门课程,当遍历到 (ti,di)(t_i, d_i)时:
  • 如果当前优先队列中所有课程的总时间与 tit_i之和小于等于 did_i ,那么就把 tit_i加入优先队列中;
  • 如果当前优先队列中所有课程的总时间与 tit_i之和大于 did_i ,那么找到优先队列中的最大元素 txjt_{x_j} 。如果 txjt_{x_j} > tit_i,则将它移出优先队列,并把 tit_i 加入优先队列中。 在遍历完成后,优先队列中包含的元素个数即为答案

注意 python中默认是最小堆,使用负数转成最大堆 c++中默认是最大堆,直接使用正数即可

  • python
class Solution:
    def scheduleCourse(self, courses: List[List[int]]) -> int:
        courses.sort(key=lambda c: c[1])

        q = []
        # 优先队列中所有课程的总时间
        total = 0

        for ti, di in courses:
            if total + ti <= di:
                total += ti
                # Python 默认是小根堆
                heapq.heappush(q, -ti)
            elif q and -q[0] > ti:
                total -= -q[0] - ti
                heapq.heappop(q)
                heapq.heappush(q, -ti)
        return len(q)
  • c++
class Solution {
public:
    int scheduleCourse(vector<vector<int>>& courses) {
        sort(courses.begin(), courses.end(), [](const auto& c0, const auto& c1) {
            return c0[1] < c1[1];
        });

        priority_queue<int> q;  // 默认是最大堆

        int total = 0;

        for(const auto & course: courses)
        {
            int ti = course[0];
            int di = course[1];
            if(total+ti <= di)
            {
                total += ti;
                q.push(ti);
            }
            else if(!q.empty() && q.top() > ti)
            {
                total -= q.top();
                total += ti;
                q.pop();
                q.push(ti);
            }
        }
        return q.size();
    }
};

复杂度分析

  • 时间复杂度:
    • O(N logN)O(N\ log N): 排序需要 O(NlogN)O(N \log N) 的时间,优先队列的单次操作需要 O(logN)O(\log N) 的时间,每个任务会最多被放入和取出优先队列一次,这一部分的时间复杂度为O(logN)O(\log N)。因此总时间复杂度也为 O(logN)O(\log N)
  • 空间复杂度:
    • O(N)O(N) : 优先队列需要使用的空间

参考