1. 课程表
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]。给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
分析:如果用有向无环图表示课程的先修关系,可以将课程 0 指向课程 1,意为完成课程 0 的前提是完成课程 1。使用 dfs 解题。首先将课程的先修关系整理好,即课程 i 的先修课程都有哪些,课程 j 的先修课又有哪些,用 vector<vector<int>>
存储这些关系。然后随便取一个课程作为入口,将其对应的 visited 数组设置为 1,意为该课程曾经访问过,再访问该课程的先修课,如果他们的 visited 数组是 1,证明他们曾经被访问过,也就是该有向图有环存在。如果他们的 visited 数组是 0,说明他们没被访问过,递归访问这些结点即可。如果当前课程以及他的先修课可以以某种顺序修完,那么将 visited 数组设置为2。
class Solution {
public:
vector<vector<int>> Relation;
vector<int> visited;
bool valid = true;
void dfs(int i)
{
visited[i]=1;
for(auto j:Relation[i])
{
if(!visited[j])
{
dfs(j);
if(valid == false)
return;
}
else if(visited[j]==1)
{
valid = false;
return;
}
}
visited[i]=2;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
Relation.resize(numCourses);
visited.resize(numCourses);
for(auto edge:prerequisites)
Relation[edge[1]].push_back(edge[0]);
for(int i=0; i<numCourses && valid; ++i)
{
if(!visited[i])
dfs(i);
}
return valid;
}
};
2. 课程表II
现在你总共有 n 门课需要选,记为 0 到 n-1。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]。给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:此题与上一题相似,只是要记录下拓扑排序的结果。在每次完成一个课程的修习后(dfs函数返回前),都将当前课程号 push 到 vector 中即可。
class Solution {
public:
vector<int> visited;
vector<vector<int>> Relation;
vector<int> ret;
bool valid = true;
int count = 0;
void dfs(int i)
{
visited[i] = 1;
for(const auto& j : Relation[i] )
{
if(!visited[j])
{
dfs(j);
if(!valid) return;
}
else if(visited[j]==1)
{
valid = false;
return;
}
}
visited[i] = 2;
ret[count] = i;
++count;
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
visited.resize(numCourses);
Relation.resize(numCourses);
ret.resize(numCourses);
for(const auto& edge:prerequisites)
Relation[edge[0]].push_back(edge[1]);
for(int i=0; i<numCourses && valid; ++i)
{
if(!visited[i])
dfs(i);
}
if(!valid){
ret.clear();
}
return ret;
}
};
3. 项目管理
公司共有 n 个项目和 m 个小组,每个项目要不没有归属,要不就由其中的一个小组负责。我们用 group[i] 代表第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)。请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:
(1) 同一小组的项目,排序后在列表中彼此相邻。
(2) 项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
结果要求:如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-items-by-groups-respecting-dependencies
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:该题目比较难的地方在于它要求同一小组的项目在排序后要相邻。所以可以根据项目间的依赖关系得出小组之间的依赖关系,从而对小组进行拓扑排序,对于没有小组认领的项目,由于可以拆分成很多个部分,出现在各小组之间,所以可以令他们独立成组。
class Solution {
public:
vector<int> ret;//TopoItem
vector<vector<int>> groupRelation;
vector<vector<int>> group2Item;
vector<int> cGroup;
vector<int> gpVisited;
vector<int> itemVisited;
vector<int> TopoGroup;
bool gpValid=true;
bool itemValid=true;
void dpsGroup(int i)
{
gpVisited[i]=1;
for(const auto& j:groupRelation[i])
{
if(!gpVisited[j])
{
dpsGroup(j);
if(!gpValid) return;
}
else if(gpVisited[j]==1)
{
gpValid = false;
return;
}
}
gpVisited[i]=2;
TopoGroup.push_back(i);
}
void dpsItem(int i, vector<vector<int>>& ItemRelation)
{
itemVisited[i]=1;
for(const auto& j:ItemRelation[i])
{
if(!itemVisited[j])
{
dpsItem(j,ItemRelation);
if(!itemValid) return;
}
else if(itemVisited[j]==1)
{
itemValid=false;
return;
}
}
itemVisited[i]=2;
ret.push_back(i);
}
vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
//整理group,使组号为-1的独立成组
cGroup.resize(n);
cGroup=group;
int count=0;
for(auto& gpid:cGroup)
{
if(gpid==-1){
gpid=m+count;
++count;
}
}
//整理各组负责的项目
group2Item.resize(m+count);
for(int i=0; i<n; ++i)
group2Item[cGroup[i]].push_back(i);
//填充groupRelation,整理组之间的拓扑关系
groupRelation.resize(m+count);
for(int i=0; i<n; ++i)
{
for(auto j:beforeItems[i])
{
if(cGroup[i]!=cGroup[j])
{
groupRelation[cGroup[i]].push_back(cGroup[j]);
}
}
}
//对组进行拓扑排序,排序结果放在TopoGroup中
gpVisited.resize(m+count);
for(int i=0; i<m+count && gpValid; ++i)
{
if(!gpVisited[i])
dpsGroup(i);
}
if(!gpValid) return ret;
//按组的拓扑排序对项目进行拓扑排序
itemVisited.resize(n);
for(int i=0; i<m+count; ++i)
{
for(const auto& item:group2Item[TopoGroup[i]])
{
if(!itemVisited[item]){
dpsItem(item, beforeItems);
if(!itemValid){
ret.clear();
return ret;
}
}
}
}
return ret;
}
};