并行课程 III【LC2050】
给你一个整数
n
,表示有n
节课,课程编号从1
到n
。同时给你一个二维整数数组relations
,其中relations[j] = [prevCoursej, nextCoursej]
,表示课程prevCoursej
必须在课程nextCoursej
之前 完成(先修课的关系)。同时给你一个下标从 0 开始的整数数组time
,其中time[i]
表示完成第(i+1)
门课程需要花费的 月份 数。请你根据以下规则算出完成所有课程所需要的 最少 月份数:
- 如果一门课的所有先修课都已经完成,你可以在 任意 时间开始这门课程。
- 你可以 同时 上 任意门课程 。
请你返回完成所有课程所需要的 最少 月份数。
**注意:**测试数据保证一定可以完成所有课程(也就是先修课的关系构成一个有向无环图)。
-
思路:拓扑排序+dp
同[课程表 II【LC210】](#课程表 II【LC210】)我们需要求出所有课程的拓扑排序,然后定义状态 d p [ i ] dp[i] dp[i]为完成某门课程的需要花费的最少月数。由于我们必须将所有的先修课 j j j上完才能开始这门课程,因此有状态转移方程
d p [ i ] = m a x ( d p [ j ] ) + t i m e [ i ] dp[i]= max(dp[j])+time[i] dp[i]=max(dp[j])+time[i]
最终答案为 m a x ( d p [ i ] ) max(dp[i]) max(dp[i]) -
实现
由于题目保证一定存在拓扑排序,因此可以边拓扑边dp
class Solution { public int minimumTime(int n, int[][] relations, int[] time) { List<Integer>[] g = new List[n]; Arrays.setAll(g, e -> new ArrayList<>()); int[] costs = new int[n]; int[] inDeg = new int[n]; Deque<Integer> queue = new LinkedList<>(); // 邻接表 for (int[] relation : relations){ int u = relation[0] - 1, v = relation[1] - 1; g[u].add(v); inDeg[v]++; } // dp计算最少月份数 修完某课程的最少月份数为其先修课的最大时间+其完成需要的时间 // dp[v] = time[v] + max(dp[u]); int res = 0; // 入度 for (int i = 0; i < n; i++){ if (inDeg[i] == 0){ queue.addLast(i); } } // 拓扑排序 while (!queue.isEmpty()){ int u = queue.pollFirst(); costs[u] += time[u]; res = Math.max(res, costs[u]); // order[index++] = u; for (int v : g[u]){ costs[v] = Math.max(costs[v], costs[u]); if (--inDeg[v] == 0){ queue.addLast(v); } } } return res; } }
-
复杂度
-
时间复杂度: O ( E + V ) O(E+V) O(E+V), E E E表示邻边的跳数,V为结点的个数
-
空间复杂度: O ( V ) O(V) O(V)
-
-