拓扑排序
拓扑排序&关键路径
前言
今天开始学习拓扑排序和关键路径,写个博客记录一下qwq
目录
-
知识讲解
-
题目模型
-
算法框架
-
例题讲解
-
习题推荐
知识搬运
在介绍拓扑排序之前,我们来引入一下
- 有向无环图
顾名思义,如果一个有向图的任意顶点都无法通过一些有向边回到自身,那么就称这个图为有向无环图(DAG)
- 拓扑序列
为了能更好地理解拓扑排序是什么,我们先通过一个栗子来感受一下:
如果你想说一口流利的英语,那你需要先学英语口语
如果你想学英语口语,那你需要多读英语文章
如果你想读英语文章,那你需要学习很多英语单词
综上,你能说一口流利英语的全过程如下(一本正经 ):
学习英语单词->多读英语文章->锻炼英语口语->流利的英语
这样的一个顺序序列就是拓扑序列
- 拓扑排序
简单来说,求得如上拓扑序列的过程就叫做拓扑排序
说得完整一点:将有向无环图\(G\)的所有顶点排成一个线性序列,使得对图\(G\)中的任意两个顶点\(u\)、\(v\),如果存在一条边\(<u,v>\),那么在这个线性序列中,\(u\)一定在\(v\)的前面的过程就叫做拓扑排序
- 作用&补充
-
因为单个任务之间可能存在多个并列关系,所以拓扑序列并不是唯一的
-
如果图\(G\)中存在环,那么拓扑排序后得到的序列元素个数将不等于原始元素个数\(N\),所以我们可以通过这个特点去判断一个图是否有环
-
生活中许多大型项目都是分步骤完成的,所以拓扑排序可以说是对此应运而生,通过求拓扑序列可以确定每个子任务的最早完成时间和最晚完成时间(也可以求得图\(G\)的最长路径)
-
其实拓扑排序更多地是和其他算法搭配使用,解决的问题也更复杂,所以一定要理解到拓扑排序的实质,不能只懂表面
- 关键路径
这个内容吧..说实话感觉就是提炼出的两种模型:\(AOV\)网和\(AOE\)网
拓扑排序加点其他算法就能搞定,就不细讲了(可能是我理解错误,欢迎大家指出)
题目模型
给定\(N\)个子任务以及任务之间的先后关系(可以推得并列关系)
-
要求解决整个问题的最短时间
-
要求求出每个子任务的最早开始时间
-
要求求出每个子任务的最晚完成时间
算法框架
-
在输入边时,存储并统计每个点的入度
-
将所有入度为0的点装入一个队列(可以是普通队列,也可以是优先队列,主要看题目需求)
-
从队列中取出对首元素\(x\),遍历所有与\(x\)相连的点\(v\),再将\(v\)的入度减一,如果减后\(v\)的入度变为0,则将\(v\)也入队
-
重复3的操作直到队列为空
最终的拓扑序列就是取出的\(x\)的顺序
例题讲解
纯纯的板子题,不过题目有点坑,必须要用\(stack\)才能通过(题目要求..大雾)
本题是多组数据啊,其他的也就是板子题了,多刷点这种无脑题板子就敲熟了
来挂一发板子(使用链式前向星存图,当然\(vector\)也行啊):
#include <bits/stdc++.h>
using namespace std;
queue<int> q;
vector<int> ans;
int n,m,u,v,tot;
int in[510005],head[510005];
struct node {
int to,net;
} e[510005];
inline void add(int u,int v) {
e[++tot].to=v;
e[tot].net=head[u];
head[u]=tot;
}
int main() {
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;i++) {
scanf("%d%d%d",&u,&v);
add(u,v);
in[v]++;
}
for(register int i=1;i<=n;i++) {
if(in[i]==0) q.push(i);
}
while(!q.empty()) {
int x=q.front();
q.pop();
ans.push_back(x);
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
in[v]--;
if(in[v]==0) q.push(v);
}
}
if(ans.size()==n) {
for(register int i=0;i<ans.size();i++) printf("%d\n",ans[i]);
}
else puts("ERROR");
return 0;
}
习题推荐
(stO 洛谷好啊 Orz)
-
洛谷P1983 车站分级 (经典好题)
-
洛谷P1038 神经网络 (经典好题)
-
洛谷P5603 小C与桌游 (计分方式很特别,思路也很巧:分别求最大代价&最大回报)
-
洛谷P3243 [HNOI2015]菜肴制作 (变相板子)
- 求最长路径除了能够使用拓扑排序求关键路径外,还可以取巧:
将边权全部取相反数,将问题转换为求解最短路!
那么我们就能够用\(SPFA\)之类的算法轻松A掉(\(Dijkstra\)当然不能用啦,因为有负权)
本题虽然是一道紫色的题,但是真的挺模板啊,感觉比车站分级、神经网络什么的简单多了