题目地址:
https://leetcode.com/problems/course-schedule-iii/
有 n n n门课 A A A,每门课有个持续时间和截止时间,以数组 ( t , d ) (t,d) (t,d)来表示。每门课必须在截止时间 d d d或者之前上完。问最多能上多少门课。同一时刻不能上两门课以上。
先将所有课按照截止日期排序。设 f [ i ] f[i] f[i]是只考虑前 i i i门课的情况下的所有合法方案中,选课个数最多并且耗时最少的那个方案的选课个数(这里合法方案是指上课没有超过截止日期,并且同一时刻只上了一门课的方案。是先从合法方案里找到选课数最多的那些,然后在从这些中找到总耗时最少的,当然满足这个条件的方案仍然可能不唯一,任选一个即可。但是我们可以证明,无论是哪个,它们含哪些课程耗时,是完全相同的)。我们考虑 f [ i + 1 ] f[i+1] f[i+1],也就是再考虑 A [ i + 1 ] A[i+1] A[i+1]这门课,首先容易知道 f [ i ] ≤ f [ i + 1 ] ≤ f [ i ] + 1 f[i]\le f[i+1]\le f[i]+1 f[i]≤f[i+1]≤f[i]+1。如果 A [ i + 1 ] A[i+1] A[i+1]这门课可以接到 f [ i ] f[i] f[i]这个方案的最后来上,并且不超过 A [ i + 1 ] A[i+1] A[i+1]的截止的话,那么就存在一个选课个数是 f [ i ] + 1 f[i]+1 f[i]+1的解了,显然这个就是最优解,并且时长也是最短的,所以此时 f [ i + 1 ] = f [ i ] + 1 f[i+1]=f[i]+1 f[i+1]=f[i]+1;如果 A [ i + 1 ] A[i+1] A[i+1]这门课接到 f [ i ] f[i] f[i]这个方案的最后来上,但是超过 A [ i + 1 ] A[i+1] A[i+1]的截止时间的话,那么 f [ i + 1 ] = f [ i ] f[i+1]=f[i] f[i+1]=f[i],由于我们想找耗时最少的方案,所以可以把之前 i i i门课再并上 A [ i + 1 ] A[i+1] A[i+1]这 i + 1 i+1 i+1门课里耗时最长的那个给踢掉,再把 A [ i + 1 ] A[i+1] A[i+1]这门课拼在后面(当然也有可能是 A [ i + 1 ] A[i+1] A[i+1]自己被踢掉了),这样仍然是个合法方案,并且耗时也是最小的。最后返回 f [ n ] f[n] f[n]即可。
具体算法可以这样来做:
1、对所有课程按截止日期从小到大排序,开个最大堆,存每门课的耗时;
2、遍历课程,并维护一个变量,记录总耗时;
3、先将当前课程接在方案后面(即入堆),并累加总耗时,如果总耗时超过了当前课程截止日期,那么就把堆中耗时最长的那个课程踢掉,并将其从总耗时中删除;
4、遍历完所有课程后返回堆的size。
代码如下:
import java.util.Arrays;
import java.util.PriorityQueue;
public class Solution {
public int scheduleCourse(int[][] courses) {
Arrays.sort(courses, (c1, c2) -> Integer.compare(c1[1], c2[1]));
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((c1, c2) -> -Integer.compare(c1, c2));
int total = 0;
for (int[] course : courses) {
total += course[0];
maxHeap.offer(course[0]);
if (total > course[1]) {
total -= maxHeap.poll();
}
}
return maxHeap.size();
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。