Given the integer n
representing the number of courses at some university labeled from 1
to n
, and the array dependencies
where dependencies[i] = [xi, yi]
represents a prerequisite relationship, that is, the course xi
must be taken before the course yi
. Also, you are given the integer k
.
In one semester you can take at most k
courses as long as you have taken all the prerequisites for the courses you are taking.
Return the minimum number of semesters to take all courses. It is guaranteed that you can take all courses in some way.
Example 1:
Input: n = 4, dependencies = [[2,1],[3,1],[1,4]], k = 2 Output: 3 Explanation: The figure above represents the given graph. In this case we can take courses 2 and 3 in the first semester, then take course 1 in the second semester and finally take course 4 in the third semester.
Example 2:
Input: n = 5, dependencies = [[2,1],[3,1],[4,1],[1,5]], k = 2 Output: 4 Explanation: The figure above represents the given graph. In this case one optimal way to take all courses is: take courses 2 and 3 in the first semester and take course 4 in the second semester, then take course 1 in the third semester and finally take course 5 in the fourth semester.
Example 3:
Input: n = 11, dependencies = [], k = 2 Output: 6
Constraints:
1 <= n <= 15
1 <= k <= n
0 <= dependencies.length <= n * (n-1) / 2
dependencies[i].length == 2
1 <= xi, yi <= n
xi != yi
- All prerequisite relationships are distinct, that is,
dependencies[i] != dependencies[j]
. - The given graph is a directed acyclic graph.
Solution: DP / Bitmask
NOTE: This is a NP problem, any polynomial-time algorithm is incorrect otherwise P = NP.
Variant 1:
dp[m] := whether state m is reachable, where m is the bitmask of courses studied.
For each semester, we enumerate all possible states from 0 to 2^n – 1, if that state is reachable, then we choose c (c <= k) courses from n and check whether we can study those courses.
If we can study those courses, we have a new reachable state, we set dp[m | courses] = true.
Time complexity: O(n*2^n*2^n) = O(n*n^4) <– This will be much smaller in practice.
and can be reduced to O(n*3^n).
Space complexity: O(2^n)
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// Author: Huahua, 52 ms, 14.5 MB class Solution { public: int minNumberOfSemesters(int n, vector<vector<int>>& dependencies, int k) { const int S = 1 << n; vector<int> deps(n); // deps[i] = dependency mask for course i. for (const auto& d : dependencies) deps[d[1] - 1] |= 1 << (d[0] - 1); // dp(i)[s] := reachable(s) at semester i. vector<int> dp(S); dp[0] = 1; for (int d = 1; d <= n; ++d) { // at most n semesters. vector<int> tmp(S); // start a new semesters. for (int s = 0; s < S; ++s) { if (!dp[s]) continue; // not a reachable state. int mask = 0; for (int i = 0; i < n; ++i) if (!(s & (1 << i)) && (s & deps[i]) == deps[i]) mask |= (1 << i); // Prunning, take all. if (__builtin_popcount(mask) <= k) { tmp[s | mask] = 1; } else { // Try all subsets. for (int c = mask; c; c = (c - 1) & mask) if (__builtin_popcount(c) <= k) { tmp[s | c] = 1; } } if (tmp.back()) return d; } dp.swap(tmp); } return -1; } }; |
Variant 2:
dp[m] := min semesters to reach state m.
dp[m | c] = min{dp[m | c], dp[m] + 1}, if we can reach m | c from m.
This allows us to get rid of enumerate n semesters.
Time complexity: O(2^n*2^n) <– This will be much smaller in practice.
and can be reduced to O(3^n).
Space complexity: O(2^n)
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Author: Huahua, 16 ms, 8.5 MB class Solution { public: int minNumberOfSemesters(int n, vector<vector<int>>& dependencies, int k) { const int kInf = 1e9; const int S = 1 << n; vector<int> deps(n); // deps[i] = dependency mask for course i. for (const auto& d : dependencies) deps[d[1] - 1] |= 1 << (d[0] - 1); // dp[m] := min semesters to reach state m. vector<int> dp(S, kInf); dp[0] = 0; for (int s = 0; s < S; ++s) { if (dp[s] == kInf) continue; // not reachable. // Generate a mask of courses we can study under state s. int mask = 0; for (int i = 0; i < n; ++i) if (!(s & (1 << i)) && (s & deps[i]) == deps[i]) mask |= (1 << i); // Prunning, take all. if (__builtin_popcount(mask) <= k) { dp[s | mask] = min(dp[s | mask], dp[s] + 1); } else { // Try all subsets. for (int c = mask; c; c = (c - 1) & mask) if (__builtin_popcount(c) <= k) dp[s | c] = min(dp[s | c], dp[s] + 1); } // Early termination? if (dp.back() != kInf) return dp.back(); } return dp.back(); } }; |
请尊重作者的劳动成果,转载请注明出处!花花保留对文章/视频的所有权利。
如果您喜欢这篇文章/视频,欢迎您捐赠花花。
If you like my articles / videos, donations are welcome.
Be First to Comment