Compute total time to finish all courses
Company: Snowflake
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates competency in graph algorithms and scheduling, including dependency resolution, cycle detection, and aggregation of task durations; it belongs to the Coding & Algorithms domain and primarily tests practical application of algorithmic reasoning with conceptual understanding of precedence constraints.
Total Time to Finish All Courses (Batch Scheduling)
Constraints
- 1 <= n <= 2 * 10^5
- 0 <= prereqs.length <= 2 * 10^5
- 1 <= time[i] <= 10^9
- Course labels are 1..n; time is 0-indexed (time[i-1] for course i)
- Return -1 if the prerequisite graph contains a cycle
Examples
Input: (4, [1, 10, 10, 1], [[1, 2], [3, 4]])
Expected Output: 20
Explanation: Batch 1 = {1, 3} duration max(1, 10) = 10; Batch 2 = {2, 4} duration max(10, 1) = 10. Total = 20.
Input: (4, [1, 10, 10, 1], [[1, 2], [2, 3], [3, 4]])
Expected Output: 22
Explanation: A single chain 1->2->3->4, so four batches of one course each: 1 + 10 + 10 + 1 = 22.
Input: (1, [5], [])
Expected Output: 5
Explanation: Single course, one batch, duration 5.
Input: (3, [3, 7, 2], [])
Expected Output: 7
Explanation: No prerequisites, so all three start in one batch; duration max(3, 7, 2) = 7.
Input: (2, [4, 6], [[1, 2], [2, 1]])
Expected Output: -1
Explanation: Courses 1 and 2 are mutually dependent (cycle). Impossible -> -1.
Input: (3, [1, 2, 3], [[1, 2], [2, 3], [3, 1]])
Expected Output: -1
Explanation: Three-node cycle 1->2->3->1. No course can start -> -1.
Input: (5, [2, 3, 8, 1, 4], [[1, 3], [2, 3], [3, 4], [3, 5]])
Expected Output: 15
Explanation: Batch 1 = {1, 2} max(2, 3) = 3; Batch 2 = {3} = 8; Batch 3 = {4, 5} max(1, 4) = 4. Total = 3 + 8 + 4 = 15.
Hints
- This is a level-by-level (BFS) topological sort. Each BFS level is exactly one batch.
- Process the queue one full level at a time: snapshot the current queue length, drain exactly that many nodes, and take the max time among them as that batch's duration.
- Detect a cycle by counting how many nodes you actually processed. If it's fewer than n, some nodes were never freed (their in-degree never hit 0), so a cycle exists -> return -1.
- Sum the per-batch maxima to get the answer.
Total Time to Finish All Courses (Async / Critical Path)
Constraints
- 1 <= n <= 2 * 10^5
- 0 <= prereqs.length <= 2 * 10^5
- 1 <= time[i] <= 10^9
- Course labels are 1..n; time is 0-indexed (time[i-1] for course i)
- Return -1 if the prerequisite graph contains a cycle
- Use 64-bit arithmetic: a chain of up to 2*10^5 courses each of time 10^9 can overflow 32-bit
Examples
Input: (4, [1, 10, 10, 1], [[1, 2], [3, 4]])
Expected Output: 11
Explanation: finish[1]=1, finish[3]=10, finish[2]=1+10=11, finish[4]=10+1=11. Max = 11 (vs 20 in the batch version).
Input: (4, [1, 10, 10, 1], [[1, 2], [2, 3], [3, 4]])
Expected Output: 22
Explanation: Single chain 1->2->3->4: finishes are 1, 11, 21, 22. Answer = 22 (same as batch here because it is one chain).
Input: (1, [5], [])
Expected Output: 5
Explanation: Single course finishes at time 5.
Input: (3, [3, 7, 2], [])
Expected Output: 7
Explanation: No prerequisites; each finishes at its own time. Max(3, 7, 2) = 7.
Input: (2, [4, 6], [[1, 2], [2, 1]])
Expected Output: -1
Explanation: Mutual dependency between 1 and 2 -> cycle -> -1.
Input: (3, [1, 2, 3], [[1, 2], [2, 3], [3, 1]])
Expected Output: -1
Explanation: Three-node cycle -> -1.
Input: (5, [2, 3, 8, 1, 4], [[1, 3], [2, 3], [3, 4], [3, 5]])
Expected Output: 15
Explanation: finish[1]=2, finish[2]=3, finish[3]=max(2,3)+8=11, finish[4]=11+1=12, finish[5]=11+4=15. Max = 15.
Input: (4, [5, 5, 1, 1], [[1, 3], [2, 3], [3, 4]])
Expected Output: 7
Explanation: finish[1]=5, finish[2]=5, finish[3]=max(5,5)+1=6, finish[4]=6+1=7. Max = 7.
Hints
- Without a batch barrier, each course finishes at time[c] + max(finish over its prerequisites). The answer is the maximum finish time across all courses -> this is the longest weighted path in the DAG.
- Do a topological sort (Kahn's algorithm). Seed roots (in-degree 0) with finish = their own time.
- When you pop a course c, relax each successor: finish[next] = max(finish[next], finish[c] + time[next]). Decrement its in-degree and enqueue it once the in-degree reaches 0.
- Cycle detection is the same as the batch version: if fewer than n nodes are processed, a cycle exists -> return -1.
- Watch for integer overflow in Java/C++ — accumulate finish times as 64-bit (long / long long).