【模板】拓扑排序 / 家谱树

题目描述

有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列,使得每个人的后辈都比那个人后列出。

输入格式

第 111 行一个整数 NNN(1≤N≤1001 \le N \le 1001≤N≤100),表示家族的人数。接下来 NNN 行,第 iii 行描述第 iii 个人的后代编号 ai,ja_{i,j}ai,j​,表示 ai,ja_{i,j}ai,j​ 是 iii 的后代。每行最后是 000 表示描述完毕。

输出格式

输出一个序列,使得每个人的后辈都比那个人后列出。如果有多种不同的序列,输出任意一种即可。

样例 #1

样例输入 #1

5
0
4 5 1 0
1 0
5 3 0
3 0

样例输出 #1

2 4 5 3 1

拓扑排序的模板题。

拓扑排序是一个有向无环图的所有顶点的线性序列。

该序列需要满足每个顶点出现且只出现一次和如果有一条 AAA 到 BBB 的路径,在序列中 AAA 出现在 BBB 的前面。

这道题目显然全部满足以上条件。如果不懂可以看下面样例的有向无环图。

拓扑排序的步骤:

  • 计算每个点的入度。

  • 入度为 000 就加入队列。

  • 当队列不为空则循环:

    • 取出队首元素并输出。

    • 遍历队首元素的连边,对应节点的入度 −1-1−1。

    • 当对应的节点入度为 000 就加入队列。

我们看样例的图,其中 111 到 555 的入度分别为 222,000,222,111 和 222。我们就把 222 号节点加入队列。队首现在不为空,所以我们取出 222 号节点,并且输出出来。这时候因为他没有祖先所以一定是现在辈分最大的所以输出出来。删除所有对应节点的入度之后我们会发现 444 号节点入度为 000。所以加入队列。然后取出队首 444 号节点并且输出。很明显他的祖先 222 号节点已经没了,所以 444 号节点已经没有辈分比他还大的了。按照上面的步骤我们就可以完成此道题目了。

#include<bits/stdc++.h>
#define For(i,a,b) for(i=a;i<=b;i++)
#define FOR(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int N = 5e5 + 10;
int n;
vector<int> p[N];
void add(int u, int v) {
	p[u].push_back(v);
}
bool vis[N];
stack<int> ans;
void dfs(int x) {
	for (auto u : p[x]) {
		if (vis[u]) continue;
		dfs(u);
	}
	vis[x] = 1;
	ans.push(x);
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int i;
	cin >> n;
	For(i, 1, n) {
		int x;
		while (cin >> x && x) {
			add(i, x);
		}
	}
	For(i, 1, n) {
		if (!vis[i])
			dfs(i);
	}
	while (ans.size()) {
		cout << ans.top() << ' ';
		ans.pop();
	}
}

更多推荐