前言:这是一道笔试题目,虽然想到了拓扑排序,但是并没有对图论进行复习,因此对这道题目印象深刻,就该题讨论一下拓扑排序的实现
小美因为各种原因无法及时返校,为了完成学业,小美只能在家里上网课,网案由n个课程视频组成,编号为1到n。每个视频都需要从头看到比才算完成该部分的学习,且某个视频只能在看完指定的一些视频之后才能开始播故。因为小美的学习能力和她用来上网课的电脑性能都很好,所以小美可以同时观看任意多个课程视频,现在小美想知道,若她不眠不休地学习,每个课程视频最早能在她开始学习多长时间之后才能结束。
输入描述
第一行有一个正整数n(1<=n<500),代表课程视频的数量。
接下来n行中的第i行开头有两个正整数L.C(1<=L<=100,0<=C
输出描述
输出一行,每一行结果都显示每个课程结束时间。数字间两两空格隔开。
样例:
输入
3
4 0
2 2 1 3
3 0
输出
4 6 3
题目转化 虽然题目用了很长时间去描述小美的故事,但是我们实质抽象去看待问题,这也就是一个图论问题。每一个课程即一个图的节点,可以同时观看任意多个视频,我们无须考虑课程数量。但是课程会受前置视频的影响,也就是说学习该课程完整视频实质上是一个有向无环图。
样例转化 我们简单的用上述样例将课程转化成有向无环图。
虽然上图有点丑,但是很好的将样例数据转化成了有向无环图。我们可以很清晰的知道:如果要学习节点1的课程,那么就需要完整学习完节点0,节点2的课程。所以节点1最终的学习时间为前置课程的最大值+本身的学习时间,即 max(4,3) + 2 = 6;
所以样例的输出为:
4 6 3
代码逻辑 利用邻接矩阵的表示方法来表示有向无环图,同时利用数组保存每个节点的入度以及每个节点的学习时间。
1:初始化领接矩阵、入度以及学习时间
2:建立队列,将入度为0,即可以立马观看的视频接入入队列
3:队列不空,则每次读出一个节点,并且将该节点所有的子节点的入度减1
4:如果入度值变为0,则加入至队列中,并且更新res数组的答案
#include
#include
#include
using namespace std;
int main(){
int n;
cin>>n;
vector<vector<int>> graph(n,vector<int>(n)); //领接矩阵表示法
vector<int> in(n,0); //入度数组
vector<int> time(n); //课程数组
vector<int> res(n,0); //结果数组
//读取输入,对上述数组进行初始化
for(int i = 0;i < n;i++){
int temp;
cin>>time[i];
temp = 0;
cin>>temp;
int father = 0;
for(int j = 0; j < temp;j++){
cin>>father;
graph[father-1][i] = 1;
in[i]++;
}
}
//建立队列,对初始的入度为0节点进行入队操作
queue<int> q;
for(int i = 0; i < n;i++){
if(in[i] == 0){
q.push(i);
res[i] = time[i];
}
}
//队列非空的情况下,不断读取节点
while(!q.empty()){
int father = q.front();
q.pop();
//给所有子节点入度进行减一,如果入度为0,则加入队列当中
for(int i = 0; i < graph[father].size();i++){
if(graph[father][i] != 0){
in[i]--;
res[i] = max(res[i],res[father]); //每一个父节点的res都需要进行一次比较,从而保存最大值
if(in[i] == 0){
res[i] += time[i];
q.push(i);
}
}
}
}
for(auto i : res){
cout<<i<<" ";
}
cout<<endl;
return 0;
}
基本的拓扑排序算法思路,如果理清后能够快速解决
时间复杂度:O(n)遍历每一个节点
空间复杂度:O(n)存储了每一个节点1次