题目描述:要求在课程最晚结束日期之前,完成该课程,并且尽可能修完 更多的课程,返回最多可以修的课程数目。
题解思路:一般题目给的是在有限条件下,尽可能多做某事,其实涉及到的就是贪心算法:在有限资源,达到利益最大化。
1.为了尽可能的完成更多的课程,所以我们必须去完成那些结束日期最靠前的课程,所以我们可以给我们的courses数组按结束日期升序排序;
2.此时我们做完courses数组的排序操作后,开始我们的选课旅程,我们先维护好一个数组,就是我们的选课数组,我把它定义为courseComplet,将我们需要选择的课程插入到里面;
3.注意此时courseComplete只需要插入对应选择课程的天数即可,因为我们需要判断的是在最晚结束日期前完成该课程,所以能够插入courseComplete的课程,都是我们能够修完的课程,所以对应课程的截至日期我们courseComplete不用关心。
那么courseComplete该如何选择课程插入呢?
其实我们判断的根据如下:
(1)遍历courses的每个课程,把每次遍历到的课程的截至日期与当前(courseComplete的数组和)+(当前遍历到的课程的持续学习时间)进行比较,如果当前遍历到的截至日期大,则转流程2,否则转流程3
(2)直接把当前遍历到的课程所需要的学习时间courses[i][0]插入courseComplete,表示我们可以修完这个课程
(3)执行到流程3,说明当前遍历到的课程,我们不能修完该课程,因为不能在截至日期前完成,此时我们应该判断:是否抛弃该课程,继续遍历下一个课程,还是把courseComplete中耗时最多的一个课程弹出,插入该课程
流程3判断依据:
当前遍历到的课程course[i]的耗时是否 大于 courseComplete数组最大的元素,如果大于,转流程3.1,否则转流程3.2
流程3.1:仅仅依靠当前遍历到的课程course[i]的耗时,并不能使我们弹出courseComplete的最大元素,插入当前遍历到的课程course[i]的耗时。我们还得判断弹出courseComplete的最大元素之后,courseComplete的元素之和加上当前遍历到的课程course[i]的耗时是否小于等于当前遍历到的课程course[i]的截止日期,如果小于等于成立,则转流程3.1.1,否则转流程3.1.2
流程3.1.1:说明我们可以弹出courseComplete耗时最多的课程,插入当前遍历到的课程,之所以这样判断,是因为我们这样操作之后,courseComplete数组中个数保持不变,而且courseComplete数组元素之和还变小了,说明我们的目前耗时变小,完成的课程数目保持不变,所以我们当然愿意这样选择。(说白一点,就是拿当前遍历到的课程的耗时与已选择的课程数组的最大元素比较,哪个小,我们就选择哪一个)
流程3.1.2:抛弃当前遍历到的课程,继续遍历下一个
流程3.2:如果小于,那我们抛弃当前遍历到的课程,继续遍历下一个
JS代码如下:
// 630 course scheduleIII[课程表 3](hard) /** * @param {number[][]} courses * @return {number} */ var scheduleCourse = function(courses) { courses.sort(function(a,b){return a[1]-b[1];});// 按课程最晚结束时间进行升序排序 let courseComplte = [0];// 当前课程已完成的持续时间的数组,加0是由于reduce需要初始值才可以执行 courses.forEach((v,i)=>{// 遍历当前课程数组 /** * 如果当前遍历到的‘课程结束时间(courses[i][1])’大于等于 当前‘已完成的持续时间的数组(courseComplte)的时间和’ + 当前遍历到的‘课程持续时间(courses[i][0])’, * 那么插入‘当前遍历到的课程的持续时间(courses[i][0])’到‘已完成的持续时间的数组(courseComplte)’ */ if(courses[i][1] >= (courses[i][0]+courseComplte.reduce((preV,curV)=>{return preV+curV})) ){ courseComplte.push(courses[i][0]); courseComplte.sort((a,b)=>{return a-b;});// 每插入一次都进行升序排序,以便于后续弹出耗时最大的课程 }else { /** * 由于前面我们已经将课程数组按结束时间进行升序排序, * 所以遍历到的课程数组元素(courses[i])的最晚时间只能大于等于前面遍历到的课程数组元素(courses[i]) * 再由于第一个if已经判断:当前遍历到的‘课程结束时间(courses[i][1])’大于等于 当前‘已完成的持续时间的数组(courseComplte)的时间和’ + 当前遍历到的‘课程持续时间(courses[i][0])’, * 所以代码运行到此处,说明当前遍历到的课程数组元素(courses[i])的最晚时间 小于 当前‘已完成的持续时间的数组(courseComplte)的时间和’ + 当前遍历到的‘课程持续时间(courses[i][0])’ * 所以此处我们需要做是否对 ‘当前课程已完成的持续时间的数组’ 进行弹出栈顶的判断(前面对courseComplte的升序排序就是为了弹出栈顶判断) * 判断流程: * 1.此时我们需要判断‘当前遍历到的课程数组元素(courses[i])’的课程耗时 是否小于 ‘已完成的持续时间的数组courseComplte’最大的耗时课程,如果小于 则转流程2 否则转流程4 * 2.再计算:(当前课程已完成的持续时间的数组的和 - 栈顶元素 + 当前遍历到的课程数组元素(courses[i])的课程耗时 ) 是否小于当前遍历到的课程数组元素的最晚结束时间,如果小于转流程3, 否则转流程4 * 3.弹出‘当前课程已完成的持续时间的数组’的栈顶,并对‘当前遍历到的课程数组元素(courses[i])的课程耗时 )’入栈到courseComplte中 * 4.结束本次循环 */ if(courses[i][0]<courseComplte[courseComplte.length-1]){ if((courses[i][0]-courseComplte[courseComplte.length-1]+courseComplte.reduce((preV,curV)=>{return preV+curV;})) < courses[i][1]){ courseComplte.pop(); courseComplte.push(courses[i][0]); courseComplte.sort((a,b)=>{return a-b;});// 没插入一次都进行升序排序,以便于后续弹出耗时最大的课程 }else{ return; } }else{ return; } } }) courseComplte.shift();// 弹出第一个元素0 return courseComplte.length; };
虽然通过了,但是效率不高~