LeetCode-Python-5435. 并行课程 II(拓扑排序 + 堆)

 

给你一个整数 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值