目录
- 题目截图
- 题目分析
- ac code
- 总结
题目截图
题目分析
- 这道题很像toposort,但实际不是
- 因为那些indeg为先为0的,先选不一定好的
- 考虑到n很小
- 我们使用状态压缩
- dfs(state)表示当前state下,还需要多少个学期结束
- 用pre数组存一下每个idx对应的前置条件二进制
- nxt表示表示state中不存在的且其pre已经满足
- floor表示这个nxt最少需要的学期数
- res记录答案
- 特别地,我们需要在nxt选出min(m, k)个作为一个组合
- new_state = state | sum(1 << j for j in item)
- cur = 1 + dfs(new_state)
- 然后res = min(res, min)
- 特别地,res = floor直接break了
ac code
class Solution:
def minNumberOfSemesters(self, n: int, relations: List[List[int]], k: int) -> int:
@cache
def dfs(state): # 当前状态为state,还需要多少个学期学完?
if state == (1 << n) - 1:
return 0
# pre[j]表示j的全部前置条件
# 用二进制记录
# nex记录所有当前状态不含的,但是前置条件已经满足的,可选的下一层节点
nex = [j for j in range(n) if not state & (1 << j) and state & pre[j] == pre[j]]
m = len(nex)
# floor表示nex最少需要修多少个学期
floor = m // k + int(m % k > 0)
res = n # 最多修n个学期
# 从nex中选min(m, k)出来作为一种组合
for item in combinations(nex, min(m, k)):
# sum(1 << j for j in item)表示这个组合对应的二进制
# cur表示选了这种组合后,需要的学期
cur = 1 + dfs(state | sum(1 << j for j in item))
res = min(res, cur)
if res == floor: # 性能优化点,提前终止,因为res不可能会小于floo热的
break
return res
pre = [0] * n
# 前置条件通过位运算存入pre
for x, y in relations:
pre[y - 1] |= 1 << (x - 1)
return dfs(0)
总结
- toposort就是误导
- 考虑n的大小,使用dfs记忆话+状态压缩!
- combinations(lst, k)表示从lst选出k个的所有组合!