课程表 III

75 阅读1分钟

630. 课程表 III - 力扣(LeetCode)

这里有 n 门不同的在线课程,按从 1n  编号。给你一个数组 courses ,其中 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 <= 10^4
  • 1 <= durationi, lastDayi <= 10^4

思路

本题使用队列优先 + 贪心算法求解。对于两门课程[t1,d1][t2, d2], 如果 d1 <= d2, 那么我们应该优先学习前者,再学习后者,总是最优解。

我们先对课程按照关闭时间升序排序,再遍历所有课程。用 q 来保存第 i 门课程之前可修的最多课程,q 中按照课程用时排序,用 total 保存 q 之和。当我们遍历到了第i门课程[ti, di], 此时 total += ti, 把ti插入到q中合适的位置,如果total > di 说明选择是不合法的,我们需要从我们选择的课程中来移除一些课程,移除哪些课程呢?当然是耗时最长的课程了 total -= q.pop(),再接着往下遍历。

解题

/**
 * @param {number[][]} courses
 * @return {number}
 */
var scheduleCourse = function (courses) {
  courses.sort((a, b) => a[1] - b[1]);
  const q = [];
  const bf = (value) => {
    let l = 0;
    let r = q.length - 1;
    while (l <= r) {
      const m = (l + r) >> 1;
      if (q[m] > value) {
        r = m - 1;
      } else if (q[m] < value) {
        l = m + 1;
      } else {
        return m;
      }
    }
    return l;
  };
  let total = 0;
  for (let i = 0; i < courses.length; i++) {
    total += courses[i][0];
    let idx = bf(courses[i][0]);
    q.splice(idx, 0, courses[i][0]);
    if (total > courses[i][1]) {
      total -= q.pop();
    }
  }
  return q.length;
};