图论基础-拓补排序

拓补排序是图论的重要基础之一。其原理是寻找有向图中入度为0的节点,找到后把它输出或存储,然后把它和它的边删掉(这时又会形成新的入度为0的节点,那么再重复以上操作直到把所有的点都输出),从而形成一个序列便于遍历等操作。当然,对于同一个图,拓补排序得到的序列可能不是唯一的。

如,对于一个有向图的节点关系:
1 —> 2
1 —> 3
1 —> 5
3 —> 2
3 —> 4
4 —> 2
我们便可以得到以下序列:
1 3 4 2 5
1 3 5 4 2

模版代码:
已知一个有向无环图有n个节点,m条边,求一个拓补序列。

#include
#include
#include
#include
using namespace std;
vector<int> a[120];//用动态数组记录点与点之间的边
int many[120];//记录每个点的入度
int n, m;
void work(int t) {
	for(int i = 0; i < a[t].size(); i++) {
		many[a[t][i]]--; //删去入度为0的点的同时也删除边,则原来与之相连的点的入度减一
	}
}
int main() {
	memset(many, 0, sizeof(many));
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		a[x].push_back(y); 
		many[y]++;
	}
	for(int i = n; i >= 1; i--) { //有n个点,为保证次序就求n次
		for(int j = 1; j <= n; j++) { //找入度为0的节点
				if(many[j] == 0) {
				printf("%d", j);
				work(j);
				many[j] = -1;
			}
		}
	}
	return 0;
}

这段代码能用来求从小到大排列的拓补序列(是在保证访问顺序的前提下)。

一道例题:
题目见codevs 2833
http://www.codevs.cn/problem/2833/
经分析得,此题的关键是求最长的拓补序列,然后再用总点数减之即可。
将模版稍作修改可得:

#include
#include
#include
using namespace std;
vector<int> a[10002];
int many[10002];
int n, m;
int count = 0;
void tb(int t) {
	for(int i = 0; i < a[t].size(); i++) {
		many[a[t][i]]--;
	}
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i++){
		int x, y;
		scanf("%d%d", &x, &y);
		a[x].push_back(y);
		many[y]++;
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++) {
			if(many[j] == 0) {
				tb(j);
				count++;
				many[j] = -1;
			}
		}
	}
	if(count == n) {
		printf("o(∩_∩)o");
	}
	else {
		printf("T_T\n%d", n - count);
	}
	return 0;
}

你可能感兴趣的:(图论)