题目
There are a total of n courses you have to take, labeled from
0
ton-1
.Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair:
[0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
示例
分析
- 这一道题题意是给出了一系列课程,这一些课程的修读具有先后次序,而所要解答的问题在于能否根据这一些次序完成课程修读。实际含义也就是判断给出的二元次序是否会形成环,如果形成环则相当于产生一个死锁。不能够修读这个环中的任何一门课(都有先修课程要求)
- 相比于课后的题目,也就是最短的修读时间,这一道题只需要进行判断能够修读完即可,所以只需要用一次dfs判断是否有回边即可
- 另外也可以使用类似于找最短修读时间来解决这一道题:时间消耗会比较大
- 首先使用vector<set<int>>来构造一个入度数组与出度数组,用来记录每一个顶点的初入度情况
- 接着创建flags数组用来记录每一门课的完成情况,如若已经完成着赋值为1
- 创建一个循环体
- 创建一个vector<int>类型,遍历所有的未完成的顶点,将入度为0的点计入其中
- 若入度的点个数为0,则:
- 如果flags全为1,则返回true
- 否则则返回false,因为这说明图中只剩下一个环
- 遍历该数组,利用出度数组删除相关的入度数组中相应的元素。
- 同时将对应的元素的flags置为1
- 重复以上步骤,直到跳出循环即可
题解
class Solution {
public:
bool canFinish(int numCourse, vector<pair<int, int>>& prerequisites) {
vector<set<int>> in = vector<set<int>>(numCourse);
vector<set<int>> out = vector<set<int>>(numCourse);
int flags[numCourse];
for(auto i: flags){
i = 1;
}
for(auto i: prerequisites){
flags[i.first] = flags[i.second] = 0;
in[i.first].insert(i.second);
out[i.second].insert(i.first);
}
vector<int> queue1;
vector<int> queue2;
while(1){
for(int i = 0; i < in.size(); i++){
if(flags[i]!= 1 && in[i].size() == 0){
queue1.push_back(i);
// cout<<i << " \n";
flags[i] = 1;
}
}
int check = 0;
for(auto i: flags){
if(i != 1){
check = 1;
break;
}
}
if(check == 0) return true;
//cout << queue1.size() << "h\n";
if(queue1.size() == 0){
for(auto i: flags){
if(i != 1){
return false;
}
}
return true;
}
for(auto i: queue1){
for(auto j: out[i]){
in[j].erase(i);
}
}
queue1.clear();
}
}
};
后记
以上方法比起dfs复杂度会偏高,而使用dfs则只需要进行一次dfs即可,所以如果要快的方法建议还是用dfs