/**
* @param {number[][]} courses
* @return {number}
* 这道题典型的用到了高中数学里的递推
* 关键:
* 1.如果两门课程[t1, d1], [t2, d2]有 d1 <= d2, 那么先学前者是最优的, 因为
* 如果存在两门课程都学的可能性的话, 先学前者就一定能接着学后者、但是先学后者不一定能再学前者。
* 起始对于多门课程也是这样, 优先学习d小的课程才可能多学,证明是类似的.
* 这就是必须排序的原因
* 2.先定义最优选择: n门课中选择的课程数k最大、k门课总耗时最短称为最优选择。
* 如果前i门课中最优选择是t1、t2...tk, 那么假设最优课程数是k +1,应该怎么处理:
* (1) 如果 t1 + t2 + ... + tk + t0 <= d0, 那么应该直接把t0加进来, 最优解变成了k + 1门课,
* 这个可用反证法证明;
* (2)如果t1 + t2 + ... + tk + t0 > d0, 那么应该把t1、t2...tk中耗时最多的一门课去掉, 再加入t0,这时最优解是k门课。同样可用反证法证明
* (3)
*/
// 排序的作用其实是找到结束时间最小的那门课
// 因为递推需要一个起始的最优解
var scheduleCourse = function(courses) {
courses.sort((a, b) => a[1] - b[1])
let total = 0
let ans = 0
const queue = new Queue()
for(let x of courses) {
if((total +x[0]) <= x[1]) {
total += x[0]
ans++
queue.add(x)
} else{
let top = queue.peek()
if(top && (top[0] > x[0])) {
queue.pop()
total = total - top[0] + x[0]
queue.add(x)
}
}
}
return ans
};
function Queue() {
this.list = []
}
// 自定义优先队列, 升序排
Queue.prototype.add = function(ele) {
let l = 0
let r = this.list.length
while(l < r) {
const m = ((l + r) / 2) | 0
if(ele[0] > this.list[m][0]) {
l = m + 1
} else if(ele[0] === this.list[m][0]) {
l = m
break
} else r = m
}
this.list.splice(l, 0, ele)
}
// 弹出队头元素
Queue.prototype.pop = function() {
this.list.pop()
}
Queue.prototype.peek = function() {
return this.list.length ? this.list[this.list.length - 1] : null
}