题目描述:
你总共需要上 numCourses
门课,课程编号依次为 0
到 numCourses-1
。你会得到一个数组 prerequisite
,其中 prerequisites[i] = [ai, bi]
表示如果你想选 bi
课程,你 必须 先选 ai
课程。
- 有的课会有直接的先修课程,比如如果想上课程
1
,你必须先上课程0
,那么会以[0,1]
数对的形式给出先修课程数对。
先决条件也可以是 间接 的。如果课程 a
是课程 b
的先决条件,课程 b
是课程 c
的先决条件,那么课程 a
就是课程 c
的先决条件。
你也得到一个数组 queries
,其中 queries[j] = [uj, vj]
。对于第 j
个查询,您应该回答课程 uj
是否是课程 vj
的先决条件。
返回一个布尔数组 answer
,其中 answer[j]
是第 j
个查询的答案。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]], queries = [[0,1],[1,0]]
输出:[false,true]
解释:课程 0 不是课程 1 的先修课程,但课程 1 是课程 0 的先修课程。
示例 2:
输入:numCourses = 2, prerequisites = [], queries = [[1,0],[0,1]]
输出:[false,false]
解释:没有先修课程对,所以每门课程之间是独立的。
示例 3:
输入:numCourses = 3, prerequisites = [[1,2],[1,0],[2,0]], queries = [[1,0],[1,2]]
输出:[true,true]
提示:
2 <= numCourses <= 100
0 <= prerequisites.length <= (numCourses * (numCourses - 1) / 2)
prerequisites[i].length == 2
0 <= ai, bi <= n - 1
ai != bi
- 每一对
[ai, bi]
都 不同 - 先修课程图中没有环。
1 <= queries.length <= 104
0 <= ui, vi <= n - 1
ui != vi
前言:
没错,又是课程表,只不过题目考的内容变了多了一点。理清题目的意思以及给的示例就要猜到这是一个有向图,又看到题目说没有环,那就容易想到拓扑图了。而且,题目中这种类似先决条件有强硬的顺序要求,容易考拓扑排序。
什么是拓扑排序?
对于任何有向图而言,其拓扑排序为其所有节点的一个线性排序(对于同一个有向图而言可能存在多个这样的节点排序)。该排序满足这样的条件:对于图中的任意两个节点u和v,若存在一条有向边从u指向v,则在拓扑排序中u一定出现在v前面。
举个例子:你的朋友来家里吃饭,你准备做饭,你要做饭,首先得买菜,买菜得去超市,去超市要出门搭车,因此这个顺序就是 出门搭车 -> 到超市 -> 买菜 -> 回家做饭。
对于题目中的先决条件和间接条件也是一个道理,如果课程 a 是课程 b 的先决条件,课程 b 是课程 c 的先决条件,要想修课程c,那么就要先修课程b,而要想修课程吧,那么就要去修课程a。所以,修这三门课的拓扑顺序就是a-->b-->c,a一定在b的前面,b一定在c的前面。
思路:
由于题目要求的是对于第 j
个查询,回答课程 uj
是否是课程 vj
的先决条件。
我们可以建立一个邻接矩阵,f[a][b]表示a-->b这条边,返回类型为bool。f[a][b]表示a是b的先决条件。
那么在哪里去更新这个f[][]呢?
对于拓扑排序这个算法我们知道,每次在遍历一条边后都会把这条边删除,当遍历到t-->j这一条边,说明t是j的先决条件,也就是f[t][j]=true。那么我们可以再去遍历一遍所有到 j 的点,看看由f[t][j]能不能推出来其他的先决条件.
f[t][j]=true; for(int h=0;h<numCourses;h++){ if(f[h][t]==true)f[h][j]=true; }
如果f[h][t]==true,说明h是t的先决条件,又由于f[t][j]=true,又能推出来f[h][j]=true.也就是当h-->t存在,t-->j存在,那么h-->j也存在。
由此,对于每一个点来说,每一个可以到这个点的边都可以被确认,这样一来,当我们把所有的边都遍历完后,可以确定的关系也就出来了。
代码:
public: int h[110],ne[10000],e[10000],idx; int d[110]; int q[110]; bool f[110][110]; int hh=0,tt=-1; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void topu(int numCourses){ while(hh<=tt){ int t=q[hh++]; for(int i=h[t];i!=-1;i=ne[i]){ int j=e[i]; f[t][j]=true; for(int h=0;h<numCourses;h++){ if(f[h][t]==true)f[h][j]=true; } d[j]--; if(d[j]==0){ q[++tt]=j; } } } } vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) { memset(h,-1,sizeof h); for(int i=0;i<numCourses;i++)d[i]=0; for(int i=0;i<10000;i++){ ne[i]=0; e[i]=0; } for(auto it:prerequisites){ int x=it[0]; int y=it[1]; add(x,y); d[y]++; } for(int i=0;i<numCourses;i++){ if(d[i]==0){ q[++tt]=i; } } topu(numCourses); vector<bool> ans; for(auto it:queries){ int x=it[0]; int y=it[1]; ans.push_back(f[x][y]); } return ans; }