给你一个整数 n
表示某所大学里课程的数目,编号为 1
到 n
,数组 dependencies
中, dependencies[i] = [xi, yi]
表示一个先修课的关系,也就是课程 xi
必须在课程 yi
之前上。同时你还有一个整数 k
。
在一个学期中,你 最多 可以同时上 k
门课,前提是这些课的先修课在之前的学期里已经上过了。
请你返回上完所有课最少需要多少个学期。题目保证一定存在一种上完所有课的方式。
示例 1:
输入:n = 4, dependencies = [[2,1],[3,1],[1,4]], k = 2 输出:3 解释:上图展示了题目输入的图。在第一个学期中,我们可以上课程 2 和课程 3 。然后第二个学期上课程 1 ,第三个学期上课程 4 。
示例 2:
输入:n = 5, dependencies = [[2,1],[3,1],[4,1],[1,5]], k = 2 输出:4 解释:上图展示了题目输入的图。一个最优方案是:第一学期上课程 2 和 3,第二学期上课程 4 ,第三学期上课程 1 ,第四学期上课程 5 。
示例 3:
输入:n = 11, dependencies = [], k = 2 输出:6
提示:
1 <= n <= 15
1 <= k <= n
0 <= dependencies.length <= n * (n-1) / 2
dependencies[i].length == 2
1 <= xi, yi <= n
xi != yi
- 所有先修关系都是不同的,也就是说
dependencies[i] != dependencies[j]
。 - 题目输入的图是个有向无环图。
思路:
类似207, 210, 1352, 1462。
题目可以抽象成节点间存在依赖关系的图,所以用拓扑排序可以解题。
首先建图,然后处理入度为0(没有先修课)的节点,
这道题特殊的地方在于,每学期学的课是有上限的,
所以如果很多课同时都可以上,根据贪心,应该先上 以这门课为先修课的课 最多的 这门课。
比如这学期只能上一门课,现在可以上A, 它是4门课的先修,也可以上B,它是2门课的先修,就应该选A。
然后还需要注意的就是,对于每门课,需要记录下来它最早什么时候才可以上,
比如现在有两门课, A 是 B 的先修,这个学期还能上两门课,上完了A之后,其实是不能上B的,因为题目要求(前提是这些课的先修课在之前的学期里已经上过了), 因此需要把这类课存起来,下个学期再上。
其他部分就是普通的拓扑排序。
时间复杂度:O(NlogN)
空间复杂度:O(N)
class Solution(object):
def minNumberOfSemesters(self, n, dependencies, k):
"""
:type n: int
:type dependencies: List[List[int]]
:type k: int
:rtype: int
"""
from collections import defaultdict
inDegree = defaultdict(int)
outDegree = defaultdict(int)
children = defaultdict(set)
for src, dec in dependencies: # 建图
inDegree[dec] += 1
outDegree[src] += 1
children[src].add(dec)
queue = []
for i in range(1, n + 1):
if inDegree[i] == 0: # 入度为0(没有先修课了)的课入队
heappush(queue, (-outDegree[i], i, -1)) # 出度越大(以这门课作为先修课的课越多),优先级越高
semesterCnt = 0
while queue:
semesterCnt += 1
nextSemesterCourses = [] # 存放这个学期不能上的课
courseCnt = 0
while courseCnt < k and queue: # 每个学期最多上 k 门课
priority, node, preFinishedSemester = heappop(queue)
if preFinishedSemester >= semesterCnt: # 当前学期不能上这门课
nextSemesterCourses.append((priority, node, preFinishedSemester))
continue
for child in children[node]: # 这门课可以学,学它,然后处理孩子课的入度
inDegree[child] -= 1
if inDegree[child] == 0: # 孩子课的先修课全上完了
heappush(queue, (-outDegree[child], child, semesterCnt))
courseCnt += 1
for item in nextSemesterCourses: # 把之前存起来的本学期不能上的课再重新入队
heappush(queue, item)
return semesterCnt