You are given an integer n
, which indicates that there are n
courses labeled from 1
to n
. You are also given a 2D integer array relations
where relations[j] = [prevCourse<sub>j</sub>, nextCourse<sub>j</sub>]
denotes that course prevCourse<sub>j</sub>
has to be completed before course nextCourse<sub>j</sub>
(prerequisite relationship). Furthermore, you are given a 0-indexed integer array time
where time[i]
denotes how many months it takes to complete the (i+1)<sup>th</sup>
course.
You must find the minimum number of months needed to complete all the courses following these rules:
Return the minimum number of months needed to complete all the courses.
Note: The test cases are generated such that it is possible to complete every course (i.e., the graph is a directed acyclic graph).
Example 1:
n = 3, relations = [[1,3],[2,3]]
, time = [3,2,5]
Output: 8
Explanation: We start course 1 and course 2 simultaneously at month 0. Course 1 takes 3 months and course 2 takes 2 months to complete respectively. Thus, the earliest time we can start course 3 is at month 3, and the total time required is 3 + 5 = 8 months.
Example 2:
n = 5, relations = [[1,5],[2,5],[3,5],[3,4],[4,5]]
, time = [1,2,3,4,5]
Output: 12
Explanation: You can start courses 1, 2, and 3 at month 0. You can complete them after 1, 2, and 3 months respectively. Course 4 can be taken only after course 3 is completed, i.e., after 3 months. It is completed after 3 + 4 = 7 months. Course 5 can be taken only after courses 1, 2, 3, and 4 have been completed, i.e., after max(1,2,3,7) = 7 months. Thus, the minimum time needed to complete all the courses is 7 + 5 = 12 months.
What is the most efficient algorithm to solve this problem?
One straightforward but inefficient approach is to consider all possible permutations of the courses and, for each permutation, check if it satisfies the prerequisite relationships. If a permutation is valid, calculate the time needed to complete all courses in that order. The minimum time among all valid permutations would be the answer.
This approach is highly inefficient due to the factorial time complexity of generating permutations.
A much more efficient solution involves topological sorting and dynamic programming. Since the graph is a directed acyclic graph (DAG), we can use topological sort to process nodes in a valid order (i.e., all prerequisites of a node are visited before the node itself). During the topological sort, we can use dynamic programming to calculate the earliest finish time for each course.
Here's the algorithm:
adj[i]
contains a list of courses that depend on course i
. Also, create an in-degree array indegree
where indegree[i]
represents the number of prerequisites for course i+1
.dp
array of size n
. dp[i]
will store the earliest finish time for course i+1
. Initialize dp[i]
with time[i]
because a course must take at least its own duration to complete.u
.v
of u
(i.e., v
depends on u
):
dp[v-1]
with max(dp[v-1], dp[u-1] + time[v-1])
. This means the earliest finish time for course v
is the maximum of its current finish time and the finish time of course u
plus the duration of course v
.indegree[v-1]
.indegree[v-1]
becomes 0, enqueue v
.dp
array will be the minimum time needed to complete all courses.time
array.n
is 1, the answer is time[0]
.from collections import deque
def min_months_to_complete_courses(n, relations, time):
adj = [[] for _ in range(n + 1)]
indegree = [0] * n
for prev, next_ in relations:
adj[prev].append(next_)
indegree[next_ - 1] += 1
dp = [t for t in time]
queue = deque([i + 1 for i in range(n) if indegree[i] == 0])
while queue:
u = queue.popleft()
for v in adj[u]:
dp[v - 1] = max(dp[v - 1], dp[u - 1] + time[v - 1])
indegree[v - 1] -= 1
if indegree[v - 1] == 0:
queue.append(v)
return max(dp)