LeetCode 1462: Course Schedule IV (Transitive Closure with Floyd-Warshall)
LeetCode 1462GraphFloyd-WarshallToday we solve LeetCode 1462 - Course Schedule IV.
Source: https://leetcode.com/problems/course-schedule-iv/
English
Problem Summary
You are given numCourses, direct prerequisite pairs [a, b] meaning a -> b, and many queries [u, v]. For each query, decide whether u is a (direct or indirect) prerequisite of v.
Key Idea
This is a classic reachability problem on a directed graph. Since we must answer many queries, precompute a boolean matrix reach[i][j] indicating whether i can reach j.
Initialize direct edges, then run Floyd-Warshall style transitive closure:
reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j]).
After precomputation, every query is answered in O(1).
Complexity
Precompute time: O(n^3), where n = numCourses. Query time: O(1) each. Space: O(n^2).
Reference Implementations (Java / Go / C++ / Python / JavaScript)
class Solution {
public List<Boolean> checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) {
boolean[][] reach = new boolean[numCourses][numCourses];
for (int[] e : prerequisites) reach[e[0]][e[1]] = true;
for (int k = 0; k < numCourses; k++) {
for (int i = 0; i < numCourses; i++) {
if (!reach[i][k]) continue;
for (int j = 0; j < numCourses; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
List<Boolean> ans = new ArrayList<>();
for (int[] q : queries) ans.add(reach[q[0]][q[1]]);
return ans;
}
}func checkIfPrerequisite(numCourses int, prerequisites [][]int, queries [][]int) []bool {
reach := make([][]bool, numCourses)
for i := range reach {
reach[i] = make([]bool, numCourses)
}
for _, e := range prerequisites {
reach[e[0]][e[1]] = true
}
for k := 0; k < numCourses; k++ {
for i := 0; i < numCourses; i++ {
if !reach[i][k] {
continue
}
for j := 0; j < numCourses; j++ {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j])
}
}
}
ans := make([]bool, len(queries))
for i, q := range queries {
ans[i] = reach[q[0]][q[1]]
}
return ans
}class Solution {
public:
vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
vector<vector<bool>> reach(numCourses, vector<bool>(numCourses, false));
for (auto &e : prerequisites) reach[e[0]][e[1]] = true;
for (int k = 0; k < numCourses; ++k) {
for (int i = 0; i < numCourses; ++i) {
if (!reach[i][k]) continue;
for (int j = 0; j < numCourses; ++j) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
vector<bool> ans;
ans.reserve(queries.size());
for (auto &q : queries) ans.push_back(reach[q[0]][q[1]]);
return ans;
}
};from typing import List
class Solution:
def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
reach = [[False] * numCourses for _ in range(numCourses)]
for a, b in prerequisites:
reach[a][b] = True
for k in range(numCourses):
for i in range(numCourses):
if not reach[i][k]:
continue
for j in range(numCourses):
reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])
return [reach[u][v] for u, v in queries]var checkIfPrerequisite = function(numCourses, prerequisites, queries) {
const reach = Array.from({ length: numCourses }, () => Array(numCourses).fill(false));
for (const [a, b] of prerequisites) reach[a][b] = true;
for (let k = 0; k < numCourses; k++) {
for (let i = 0; i < numCourses; i++) {
if (!reach[i][k]) continue;
for (let j = 0; j < numCourses; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
return queries.map(([u, v]) => reach[u][v]);
};中文
题目概述
给定课程数量 numCourses、先修关系 [a, b](表示 a -> b)以及若干查询 [u, v]。对每个查询判断:u 是否是 v 的直接或间接先修课。
核心思路
这是有向图上的可达性问题。由于查询很多,不能每次查询都单独 DFS/BFS。我们先做一次传递闭包预处理。
定义 reach[i][j] 表示课程 i 能否到达课程 j。
先标记所有直接边,再用 Floyd-Warshall 方式更新:
reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])。
这样每个查询都能 O(1) 返回答案。
复杂度分析
预处理时间复杂度 O(n^3),查询单次 O(1),空间复杂度 O(n^2)。
参考实现(Java / Go / C++ / Python / JavaScript)
class Solution {
public List<Boolean> checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) {
boolean[][] reach = new boolean[numCourses][numCourses];
for (int[] e : prerequisites) reach[e[0]][e[1]] = true;
for (int k = 0; k < numCourses; k++) {
for (int i = 0; i < numCourses; i++) {
if (!reach[i][k]) continue;
for (int j = 0; j < numCourses; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
List<Boolean> ans = new ArrayList<>();
for (int[] q : queries) ans.add(reach[q[0]][q[1]]);
return ans;
}
}func checkIfPrerequisite(numCourses int, prerequisites [][]int, queries [][]int) []bool {
reach := make([][]bool, numCourses)
for i := range reach {
reach[i] = make([]bool, numCourses)
}
for _, e := range prerequisites {
reach[e[0]][e[1]] = true
}
for k := 0; k < numCourses; k++ {
for i := 0; i < numCourses; i++ {
if !reach[i][k] {
continue
}
for j := 0; j < numCourses; j++ {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j])
}
}
}
ans := make([]bool, len(queries))
for i, q := range queries {
ans[i] = reach[q[0]][q[1]]
}
return ans
}class Solution {
public:
vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
vector<vector<bool>> reach(numCourses, vector<bool>(numCourses, false));
for (auto &e : prerequisites) reach[e[0]][e[1]] = true;
for (int k = 0; k < numCourses; ++k) {
for (int i = 0; i < numCourses; ++i) {
if (!reach[i][k]) continue;
for (int j = 0; j < numCourses; ++j) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
vector<bool> ans;
ans.reserve(queries.size());
for (auto &q : queries) ans.push_back(reach[q[0]][q[1]]);
return ans;
}
};from typing import List
class Solution:
def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
reach = [[False] * numCourses for _ in range(numCourses)]
for a, b in prerequisites:
reach[a][b] = True
for k in range(numCourses):
for i in range(numCourses):
if not reach[i][k]:
continue
for j in range(numCourses):
reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])
return [reach[u][v] for u, v in queries]var checkIfPrerequisite = function(numCourses, prerequisites, queries) {
const reach = Array.from({ length: numCourses }, () => Array(numCourses).fill(false));
for (const [a, b] of prerequisites) reach[a][b] = true;
for (let k = 0; k < numCourses; k++) {
for (let i = 0; i < numCourses; i++) {
if (!reach[i][k]) continue;
for (let j = 0; j < numCourses; j++) {
reach[i][j] = reach[i][j] || (reach[i][k] && reach[k][j]);
}
}
}
return queries.map(([u, v]) => reach[u][v]);
};
Comments