拓扑排序简单来说就是对于存在依赖关系的事务, 需要先完成的排在前面, 是离散数学中的一个偏序关系, 举个例子先穿袜子, 再穿鞋子.
关于它的概念, 只需要掌握上述这么多就够用了, 那么该如何应用拓扑排序呢?
图是什么你知道吧? 那入度是什么你总知道吧? 知道请看下面的分析
算法思路:
1. 我们只需要将一个图中入度为0的点(即没有前导依赖的点)拿出来
2. 然后删掉与它相连的点之间的边
3. 重复步骤 1
然后我们就得到了一个拓扑排序, 很显然, 若一个图中度为0 的点有好几个的话, 那么你取得顺序会有不同, 故拓扑排序顺序并不唯一, 但必须满足依赖关系
如果看了上面的你还不懂, 那么下面我以一道例题举个例子, 包你入门拓扑排序
有一堆杂务, 有些杂务需要完成一些准备工作才可以做, 假设不相关的杂务可以并行执行, 你的人手无限, 计算所有杂务都被完成的最短时间
输入:
第一行: 杂务数目
接下来n行: 每行都有一些用空格隔开的数, 分别表示, 工作序号, 完成需要时间, 一些必须完成的准备工作, 以一个数字0结尾.
此题需要的数据结构
int n, indgree[MAXN], len[MAXN], dp[MAXN], ans; //indgree数组维护的是每一个点的入度, len存放任务i需要时间, dp动态规划需要的数组
vector <int> relyOn[MAXN]; //第i个存依赖于任务i的任务向量数组
queue <int> q; //存放入度为0, 即前导依赖任务都完成了的任务
1. 预处理
在读入数据时, 记录每个任务的入度以及依赖于该任务的任务
int id, e;
for (int i = 1; i <= n; i++) {
cin >> id;
cin >> len[id];
while (cin >> e && e) {
relyOn[e].push_back(id);
indgree[id]++;
}
}
2. 拓扑排序
首先找到入度为0的点, 加入队列, 然后遍历队列, 这个动态规划的步骤仔细看一下应该不难理解
void topoSort() {
for (int i = 1; i <= n; i++) {
if (indgree[i] == 0) {
q.push(i);
dp[i] = len[i];
}
}
while (!q.empty()) {
int now = q.front();
q.pop();
for (int i = 0; i < relyOn[now].size(); i++) {
int nex = relyOn[now][i];
dp[nex] = max(dp[nex], dp[now] + len[nex]);
indgree[nex]--;
if (indgree[nex] == 0) {
q.push(nex);
}
}
}
}
3. 遍历dp数组, 找到最大值, 即为答案
for (int i = 1; i <= n; i++) {
ans = max(ans, dp[i]);
}
cout << ans;
return 0;