LeetCode 题解工作台
并行课程 II
给你一个整数 n 表示某所大学里课程的数目,编号为 1 到 n ,数组 relations 中, relations[i] = [x i , y i ] 表示一个先修课的关系,也就是课程 x i 必须在课程 y i 之前上。同时你还有一个整数 k 。 在一个学期中,你 最多 可以同时上 k 门课,前…
4
题型
4
代码语言
3
相关题
当前训练重点
困难 · 状态·转移·动态规划
答案摘要
我们用数组 表示课程 的先修课程的集合。由于数据规模 $n\lt 15$,我们可以用一个整数的二进制位(状态压缩)来表示集合,其中第 位为 表示课程 是课程 的先修课程。 我们用一个状态变量 表示当前已经上过的课程的集合,初始时 。如果 ,表示所有课程都上过了,返回当前学期即可。
Interview AiBoxInterview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路
题目描述
给你一个整数 n 表示某所大学里课程的数目,编号为 1 到 n ,数组 relations 中, relations[i] = [xi, yi] 表示一个先修课的关系,也就是课程 xi 必须在课程 yi 之前上。同时你还有一个整数 k 。
在一个学期中,你 最多 可以同时上 k 门课,前提是这些课的先修课在之前的学期里已经上过了。
请你返回上完所有课最少需要多少个学期。题目保证一定存在一种上完所有课的方式。
示例 1:

输入:n = 4, relations = [[2,1],[3,1],[1,4]], k = 2 输出:3 解释:上图展示了题目输入的图。在第一个学期中,我们可以上课程 2 和课程 3 。然后第二个学期上课程 1 ,第三个学期上课程 4 。
示例 2:

输入:n = 5, relations = [[2,1],[3,1],[4,1],[1,5]], k = 2 输出:4 解释:上图展示了题目输入的图。一个最优方案是:第一学期上课程 2 和 3,第二学期上课程 4 ,第三学期上课程 1 ,第四学期上课程 5 。
示例 3:
输入:n = 11, relations = [], k = 2 输出:6
提示:
1 <= n <= 151 <= k <= n0 <= relations.length <= n * (n-1) / 2relations[i].length == 21 <= xi, yi <= nxi != yi- 所有先修关系都是不同的,也就是说
relations[i] != relations[j]。 - 题目输入的图是个有向无环图。
解题思路
方法一:状态压缩 + BFS + 子集枚举
我们用数组 表示课程 的先修课程的集合。由于数据规模 ,我们可以用一个整数的二进制位(状态压缩)来表示集合,其中第 位为 表示课程 是课程 的先修课程。
我们用一个状态变量 表示当前已经上过的课程的集合,初始时 。如果 ,表示所有课程都上过了,返回当前学期即可。
如果课程 的先修课程 的集合是 的子集,说明课程 可以上。这样我们可以找到当前 状态的下一个状态 ,表示后续学期可以上的课程集合。
如果 的二进制表示中 的个数小于等于 ,说明后续学期可以上的课程数不超过 ,我们就可以将 加入队列中。否则,说明后续学期可以上的课程数超过 ,那么我们就应该从后续可以上的课程中选择 门课程,这样才能保证后续学期可以上的课程数不超过 。我们可以枚举 的所有子集,将子集加入队列中。
时间复杂度 ,空间复杂度 。其中 是题目给定的课程数。
class Solution:
def minNumberOfSemesters(self, n: int, relations: List[List[int]], k: int) -> int:
d = [0] * (n + 1)
for x, y in relations:
d[y] |= 1 << x
q = deque([(0, 0)])
vis = {0}
while q:
cur, t = q.popleft()
if cur == (1 << (n + 1)) - 2:
return t
nxt = 0
for i in range(1, n + 1):
if (cur & d[i]) == d[i]:
nxt |= 1 << i
nxt ^= cur
if nxt.bit_count() <= k:
if (nxt | cur) not in vis:
vis.add(nxt | cur)
q.append((nxt | cur, t + 1))
else:
x = nxt
while nxt:
if nxt.bit_count() == k and (nxt | cur) not in vis:
vis.add(nxt | cur)
q.append((nxt | cur, t + 1))
nxt = (nxt - 1) & x
复杂度分析
| 指标 | 值 |
|---|---|
| 时间 | complexity is O(n * 2^n * C(n, k)) due to iterating over all states and subsets of available courses, while space complexity is O(2^n) to store DP values for each state. |
| 空间 | Depends on the final approach |
面试官常问的追问
外企场景- question_mark
The problem expects a state transition dynamic programming solution using bitmask representation of course completion.
- question_mark
Consider carefully which courses can be taken each semester based on prerequisites and k-course limits.
- question_mark
Enumerating subsets efficiently and avoiding redundant DP updates demonstrates a strong grasp of the problem pattern.
常见陷阱
外企场景- error
Failing to track prerequisites accurately can lead to invalid semester counts.
- error
Not limiting course selection to k per semester can produce incorrect solutions.
- error
Redundant state updates without pruning subsets increase runtime and memory usage unnecessarily.
进阶变体
外企场景- arrow_right_alt
Change k dynamically each semester, requiring adaptive DP subset handling.
- arrow_right_alt
Introduce weighted courses where semesters count differently based on course load.
- arrow_right_alt
Allow optional courses with alternative prerequisites, increasing state complexity.