🧠
Brute Force (Backtracking with Cycle Detection)
💡 This approach tries all possible orders of taking courses to find the minimum semesters. It is extremely inefficient but helps understand the problem deeply and why we need better methods.
Intuition
Try every possible sequence of courses respecting prerequisites and count semesters. If a cycle is detected, return -1.
Algorithm
- Build a graph from the relations.
- Use backtracking to try all possible course orders that respect prerequisites.
- Keep track of courses taken and semesters used.
- If all courses are taken, update the minimum semesters; if a cycle is detected, return -1.
💡 This algorithm is hard because it tries all permutations and must carefully check prerequisites and cycles, which is exponential in time.
from collections import defaultdict
def min_semesters_brute_force(n, relations):
graph = defaultdict(list)
indegree = [0] * (n + 1)
for u, v in relations:
graph[u].append(v)
indegree[v] += 1
min_semesters = [float('inf')]
def backtrack(taken, indeg, semester):
if len(taken) == n:
min_semesters[0] = min(min_semesters[0], semester)
return
# Find all courses with indegree 0 and not taken
available = [i for i in range(1, n + 1) if indeg[i] == 0 and i not in taken]
if not available:
return
# Try all subsets of available courses (simulate parallel courses)
# For brute force, try taking each course individually (to keep it simple)
for course in available:
taken.add(course)
new_indeg = indeg[:]
for nei in graph[course]:
new_indeg[nei] -= 1
backtrack(taken, new_indeg, semester + 1)
taken.remove(course)
backtrack(set(), indegree, 0)
return min_semesters[0] if min_semesters[0] != float('inf') else -1
# Example usage
if __name__ == '__main__':
print(min_semesters_brute_force(3, [[1,3],[2,3]])) # Output: 2
print(min_semesters_brute_force(3, [[1,2],[2,3],[3,1]])) # Output: -1
Line Notes
graph = defaultdict(list)Initialize adjacency list to represent course dependencies
indegree = [0] * (n + 1)Track number of prerequisites for each course
for u, v in relations:Build graph and indegree array from relations
def backtrack(taken, indeg, semester):Recursive function to try all course taking sequences
available = [i for i in range(1, n + 1) if indeg[i] == 0 and i not in taken]Find courses ready to be taken this semester
for course in available:Try taking each available course individually to simulate parallelism
taken.add(course)Mark course as taken
new_indeg = indeg[:]Copy indegree array to update for next recursion
for nei in graph[course]:Decrease indegree of dependent courses after taking current course
backtrack(taken, new_indeg, semester + 1)Recurse to next semester after taking a course
taken.remove(course)Backtrack to try other course combinations
import java.util.*;
public class ParallelCoursesBruteForce {
static int n;
static List<List<Integer>> graph;
static int[] indegree;
static int minSemesters = Integer.MAX_VALUE;
public static int minSemestersBruteForce(int courses, int[][] relations) {
n = courses;
graph = new ArrayList<>();
for (int i = 0; i <= n; i++) graph.add(new ArrayList<>());
indegree = new int[n + 1];
for (int[] rel : relations) {
graph.get(rel[0]).add(rel[1]);
indegree[rel[1]]++;
}
backtrack(new HashSet<>(), indegree, 0);
return minSemesters == Integer.MAX_VALUE ? -1 : minSemesters;
}
private static void backtrack(Set<Integer> taken, int[] indeg, int semester) {
if (taken.size() == n) {
minSemesters = Math.min(minSemesters, semester);
return;
}
List<Integer> available = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0 && !taken.contains(i)) available.add(i);
}
if (available.isEmpty()) return;
for (int course : available) {
taken.add(course);
int[] newIndeg = indeg.clone();
for (int nei : graph.get(course)) newIndeg[nei]--;
backtrack(taken, newIndeg, semester + 1);
taken.remove(course);
}
}
public static void main(String[] args) {
System.out.println(minSemestersBruteForce(3, new int[][]{{1,3},{2,3}})); // 2
System.out.println(minSemestersBruteForce(3, new int[][]{{1,2},{2,3},{3,1}})); // -1
}
}
Line Notes
graph = new ArrayList<>()Initialize adjacency list for graph
indegree = new int[n + 1]Array to count prerequisites for each course
for (int[] rel : relations)Build graph and indegree from input relations
backtrack(new HashSet<>(), indegree, 0)Start backtracking with empty taken set and initial indegree
List<Integer> available = new ArrayList<>()Find courses with no remaining prerequisites
for (int course : available)Try taking each available course individually
int[] newIndeg = indeg.clone()Clone indegree array to update for recursion
for (int nei : graph.get(course)) newIndeg[nei]--Decrease indegree of dependent courses after taking current course
backtrack(taken, newIndeg, semester + 1)Recurse to next semester
taken.remove(course)Backtrack to try other options
#include <bits/stdc++.h>
using namespace std;
int n;
vector<vector<int>> graph;
int minSemesters = INT_MAX;
void backtrack(unordered_set<int>& taken, vector<int>& indeg, int semester) {
if ((int)taken.size() == n) {
minSemesters = min(minSemesters, semester);
return;
}
vector<int> available;
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0 && taken.find(i) == taken.end()) available.push_back(i);
}
if (available.empty()) return;
for (int course : available) {
taken.insert(course);
vector<int> newIndeg = indeg;
for (int nei : graph[course]) newIndeg[nei]--;
backtrack(taken, newIndeg, semester + 1);
taken.erase(course);
}
}
int minSemestersBruteForce(int courses, vector<pair<int,int>>& relations) {
n = courses;
graph.assign(n + 1, vector<int>());
vector<int> indeg(n + 1, 0);
for (auto& rel : relations) {
graph[rel.first].push_back(rel.second);
indeg[rel.second]++;
}
unordered_set<int> taken;
backtrack(taken, indeg, 0);
return minSemesters == INT_MAX ? -1 : minSemesters;
}
int main() {
vector<pair<int,int>> rel1 = {{1,3},{2,3}};
cout << minSemestersBruteForce(3, rel1) << "\n"; // 2
vector<pair<int,int>> rel2 = {{1,2},{2,3},{3,1}};
cout << minSemestersBruteForce(3, rel2) << "\n"; // -1
return 0;
}
Line Notes
graph.assign(n + 1, vector<int>())Initialize adjacency list for graph
vector<int> indeg(n + 1, 0)Indegree array to count prerequisites
for (auto& rel : relations)Build graph and indegree from input
void backtrack(unordered_set<int>& taken, vector<int>& indeg, int semester)Recursive backtracking function
vector<int> availableFind courses with zero indegree and not taken
for (int course : available)Try taking each available course individually
vector<int> newIndeg = indegCopy indegree array for recursion
for (int nei : graph[course]) newIndeg[nei]--Update indegree after taking course
backtrack(taken, newIndeg, semester + 1)Recurse to next semester
taken.erase(course)Backtrack to try other options
function minSemestersBruteForce(n, relations) {
const graph = Array.from({length: n + 1}, () => []);
const indegree = Array(n + 1).fill(0);
for (const [u, v] of relations) {
graph[u].push(v);
indegree[v]++;
}
let minSemesters = Infinity;
function backtrack(taken, indeg, semester) {
if (taken.size === n) {
minSemesters = Math.min(minSemesters, semester);
return;
}
const available = [];
for (let i = 1; i <= n; i++) {
if (indeg[i] === 0 && !taken.has(i)) available.push(i);
}
if (available.length === 0) return;
for (const course of available) {
taken.add(course);
const newIndeg = indeg.slice();
for (const nei of graph[course]) newIndeg[nei]--;
backtrack(taken, newIndeg, semester + 1);
taken.delete(course);
}
}
backtrack(new Set(), indegree, 0);
return minSemesters === Infinity ? -1 : minSemesters;
}
// Example usage
console.log(minSemestersBruteForce(3, [[1,3],[2,3]])); // 2
console.log(minSemestersBruteForce(3, [[1,2],[2,3],[3,1]])); // -1
Line Notes
const graph = Array.from({length: n + 1}, () => [])Initialize adjacency list for graph
const indegree = Array(n + 1).fill(0)Indegree array to track prerequisites
for (const [u, v] of relations)Build graph and indegree from input
function backtrack(taken, indeg, semester)Recursive backtracking function
const available = []Find courses with zero indegree and not taken
for (const course of available)Try taking each available course individually
const newIndeg = indeg.slice()Copy indegree array for recursion
for (const nei of graph[course]) newIndeg[nei]--Update indegree after taking course
backtrack(taken, newIndeg, semester + 1)Recurse to next semester
taken.delete(course)Backtrack to try other options
TimeO(n!) in worst case due to trying all permutations
SpaceO(n) for recursion stack and bookkeeping
The brute force tries all sequences of courses respecting prerequisites, which is factorial in n, making it impractical for large inputs.
💡 For n=10, this approach could try up to 3,628,800 sequences, which is too large to finish in reasonable time.
Interview Verdict: TLE
This approach is too slow for large inputs but is useful to understand the problem and motivate better solutions.