题目描述:
给你一个整数 n ,表示有 n 节课,课程编号从 1 到 n 。同时给你一个二维整数数组 relations ,其中 relations[j] = [prevCoursej, nextCoursej] ,表示课程 prevCoursej 必须在课程 nextCoursej 之前 完成(先修课的关系)。同时给你一个下标从 0 开始的整数数组 time ,其中 time[i] 表示完成第 (i+1) 门课程需要花费的 月份 数。
请你根据以下规则算出完成所有课程所需要的 最少 月份数:
如果一门课的所有先修课都已经完成,你可以在 任意 时间开始这门课程。
你可以 同时 上 任意门课程 。
请你返回完成所有课程所需要的 最少 月份数。
注意:测试数据保证一定可以完成所有课程(也就是先修课的关系构成一个有向无环图)。
方法一:
class Solution{
public:
struct course
{
list<int> adj;
int starttime; //开始时间,等于其所有先修课中的最晚结束时间
int endtime; //结束时间
int spendtime; //该课程需花费的时间
};
int minimumTime(int n, vector<vector<int>>&relations, vector<int>&time) {
int m = relations.size(); //m等于边数
vector<course> cous(n + 1);
vector<int> innum(n + 1); //存储各个点的入度
queue<int> que; //用队列存储入度为0的点
//存图
for (int i = 0; i < m; i++)
{
//一条a -> b的边
int a = relations[i][0];
int b = relations[i][1];
cous[a].adj.push_back(b);
innum[b]++;
}
//初始化每个点的开始、结束、花费时间
for (int i = 0; i < n; i++)
{
cous[i + 1].starttime = 0;
cous[i + 1].endtime = time[i];
cous[i + 1].spendtime = time[i];
}
for (int i = 1; i <= n; i++)
{
if (innum[i] == 0)
{
que.push(i);
}
}
while (!que.empty())
{
//找出一个入度为0的点a,将点a和与a相连的边都删去
int a = que.front();
que.pop();
list<int>::iterator iter;
for (iter = cous[a].adj.begin(); iter != cous[a].adj.end(); iter++)
{
int b = *iter;
//课程a是课程b的先修课,如果课程a的结束时间大于课程b的开始时间,就更新课程b的开始时间和结束时间
if (cous[a].endtime > cous[b].starttime)
{
cous[b].starttime = cous[a].endtime;
cous[b].endtime = cous[b].starttime + cous[b].spendtime;
}
//删去边之后,节点b的入度减一,若为0则入队
innum[b]--;
if (innum[b] == 0)
{
que.push(b);
}
}
}
//完成所有课程的最少时间,就是课程中最晚结束的课程的结束时间
int ans = INT_MIN;
for (int i = 1; i <= n; i++)
{
ans = max(ans, cous[i].endtime);
}
return ans;
}
};
很容易想到要用拓扑排序来解决,有关拓扑排序的内容参考:
拓扑排序入门(真的很简单)_安得广厦千万间的博客-CSDN博客_拓扑排序
在拓扑排序的基础上,只需要加入每一门课程的开始时间、结束时间和花费时间即可,在使用拓扑排序的过程中顺便更新,最后需要的完成所有课程的最少时间,就是所有课程中最晚结束的那门课程的最晚结束时间(有关最早开始时间、最晚结束时间的概念,我记得是曾在数据结构一门课中学过的有关aoe图和关键路径的知识)。
566

被折叠的 条评论
为什么被折叠?



