Bootstrap

拓扑排序模板题:洛谷-家谱树

原题链接:B3644 【模板】拓扑排序 / 家谱树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目:

题目描述

输入格式

输出格式

输入输出样例

思路: 

AC代码:


题目:

 

题目描述

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

输入格式

第 1 行一个整数 𝑁(1≤𝑁≤100),表示家族的人数。接下来 𝑁 行,第 𝑖 行描述第 𝑖 个人的后代编号 𝑎𝑖,𝑗,表示 𝑎𝑖,𝑗是 𝑖 的后代。每行最后是 0 表示描述完毕。

输出格式

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

输入输出样例

输入 

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

输出 

2 4 

思路: 

这道题是拓扑排序的模板题,所以思路主要介绍拓扑排序。

拓扑排序并不是类似于快排、冒泡给一串数字从大到小或者从小到大排序。拓扑排序的目的是:在一张有向无环图中,排出一个序列满足:图中的每一条有向边{x, y},x 在我们排出的序列中都出现在 y 之前,得到的序列我们成为拓扑序列。

为什么一定是有向无环图呢?这个问题留到后面回答。

拓扑排序的思想是:每次选中入度为0的点,删除这个点和它的出边,并把它加入拓扑序列

我们对这张有向无环图进行拓扑排序:

1.选中A,删除A与A的出边,把A加入拓扑序列,图片如下:

2.B和E都是入度为0的点,我们任意选一个就可以了,不难看出,拓扑序列不是唯一的,可能有多种。这里我们选B。

3.接下来选E,重复上述操作。当前的拓扑序列是{A,B,E}.

4.不难看出,最后得到的拓扑序列是{A,B,E,D,C}.

我们回到刚才的问题: 为什么一定是有向无环图呢?

我们以这张图为例:

接下来进行拓扑排序:

按照拓扑排序的思想,我们应该找到一个入度为0的点,而在这张图中我们找不到入度为0的点,这是因为图中出现了环。我们对它进行拓扑排序,最后得到的是一个空的拓扑序列。

根据这个特点,我们可以使用拓扑排序来判断图中是否有环,即根据最后得到的拓扑序列中元素个数是否与图中结点的个数相等。如果相等,即无环,不相等则有环。

 拓扑排序的模板代码:

int h[N], e[M], nx[M], tp[N];
int idx = 0, cnt = 0;
int dx[N];
int n;

void add(int x, int y) {
	e[idx] = y;
	nx[idx] = h[x];
	h[x] = idx;
	idx++;
	dx[y]++;//出度加一
}

bool topsort() {
	queue<int>q;
	for (int i = 1; i <= n; i++) {
		if (dx[i] == 0) q.push(i);//入度为0则入队
	}

	while (!q.empty()) {
		int x = q.front(); q.pop();
		tp[cnt++] = x;
		for (int i = h[x]; i != -1; i = nx[i]) {
			if (--dx[e[i]] == 0) q.push(e[i]);
		}
	}

	return cnt == n;//判断是否有环
}

代码的核心思想是使用队列来维护一个入度为0的结点的集合。

其中dx[x]是结点x的入度,tp[]存放拓扑序列,使用数据模拟邻接表的方法存储图。

不了解数组模拟邻接表的可以看我上一篇博客:AcWing-1562.微博转发-再谈图的存储结构-CSDN博客 

AC代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define MEM(x, y) memset(x, y, sizeof x)
using namespace std;

const int N = 105, M = 1e4 + 5;
int h[N], e[M], nx[M], tp[N];
int idx = 0, cnt = 0;
int dx[N];
int n;

void add(int x, int y) {
	e[idx] = y;
	nx[idx] = h[x];
	h[x] = idx;
	idx++;
	dx[y]++;
}

bool topsort() {
	queue<int>q;
	for (int i = 1; i <= n; i++) {
		if (dx[i] == 0) q.push(i);
	}

	while (!q.empty()) {
		int x = q.front(); q.pop();
		tp[cnt++] = x;
		for (int i = h[x]; i != -1; i = nx[i]) {
			if (--dx[e[i]] == 0) q.push(e[i]);
		}
	}

	return cnt == n;
}


int main() {
	MEM(h, -1);
	MEM(dx, 0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int temp = 0; cin >> temp;
		while (temp != 0) {
			add(i, temp);
			cin >> temp;
		}
	}

	topsort();

	for (int i = 0; i < cnt; i++) {
		cout << tp[i] << " ";
	}
	cout << endl;
	
	return 0;
}

此题还有更多更优解法,不再一一介绍了,文章尚有不足,欢迎大佬们指正。

;