浅谈拓扑排序(模板+例题)

所谓拓扑排序就是对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。

可能这个定义有点难理解,那接下来我就说说它的具体流程。(真的容易理解的)

进入正题

随便的给你一个有向无环图

1:找到入度为0的点

就像此图,它入度为0的点是1和4,将它们取出来,记为第一个数和第二个数。并且把与它们有关的边都去掉(这个图就没有哈)

2:重复上述操作。

取出入度为0的点,现在入度为0的点是2,5,把它们记作第3个数和第4个数。把它们相邻的边去掉。

 最后一个数就是3了。

根据取出的顺序进行排序,就是1,4,2,5,3.

所以拓扑排序是不是很容易呢?

接下来就给出拓扑排序的模板。

1:初始化模板。

	for(int i=1; i<=m; ++i) {//m条边 
			cin>>x>>y;
			edge[x].push_back(y);
			in[y]++;//in[i]表示入度 
		}
		for(int i=1; i<=n; ++i) {//n个点 
			if(in[i]==0) {
				q.push(i);//入度为0就把它加入队列 
			}
		}

2:拓扑排序函数模板

void topology_sort() {
	while(!q.empty()) {
		int t=q.front();
		ans.push_back(t);
		q.pop();
		for(int i=0; i<edge[t].size(); ++i) {
			in[edge[t][i]]--;
			if(in[edge[t][i]]==0) q.push(edge[t][i]);
		}
	}
}

其实拓扑排序最大的好处是判断有无环。我们可以根据取出来点的数量和点的个数进行比较。

如果取出来点的数量和点的个数相同,那么它是无环的。如果取出来点的数量小于点的个数则是有环的。

来上模板题!!!

题目链接:拓扑排序模板题

题意:给你n个点,m条边,对它们拓扑排序。输出排序结果。

解析:没啥好说的,直接上模板代码。反正这题记得初始化清空就行了。

#include<bits/stdc++.h>
#include<queue>
#include<vector>
using namespace std;
int in[105];
queue<int>q;
vector<int>edge[105];
vector<int>ans;
void topology_sort() {
	while(!q.empty()) {
		int t=q.front();
		ans.push_back(t);
		q.pop();
		for(int i=0; i<edge[t].size(); ++i) {
			in[edge[t][i]]--;
			if(in[edge[t][i]]==0) q.push(edge[t][i]);
		}
	}
}
int main( ) {
	int n,m,x,y;
	while(cin>>n>>m) {
		ans.clear();
		for(int i=1; i<=100; ++i) {
			edge[i].clear();
		}
		memset(in,0,sizeof(in));
		if(n==0&&m==0) return 0;
		for(int i=1; i<=m; ++i) {//m条边 
			cin>>x>>y;
			edge[x].push_back(y);
			in[y]++;//in[i]表示入度 
		}
		for(int i=1; i<=n; ++i) {//n个点 
			if(in[i]==0) {
				q.push(i);//入度为0就把它加入队列 
			}
		}
		topology_sort();
		for(int i=0; i<ans.size(); ++i) {
			cout<<ans[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

来道例题

题目链接:稍难的拓扑排序

这是一道cf上面的题目,居然有1800分的难度。

C. Book

You are given a book with nn chapters.

Each chapter has a specified list of other chapters that need to be understood in order to understand this chapter. To understand a chapter, you must read it after you understand every chapter on its required list.

Currently you don't understand any of the chapters. You are going to read the book from the beginning till the end repeatedly until you understand the whole book. Note that if you read a chapter at a moment when you don't understand some of the required chapters, you don't understand this chapter.

Determine how many times you will read the book to understand every chapter, or determine that you will never understand every chapter no matter how many times you read the book.

Input

Each test contains multiple test cases. The first line contains the number of test cases t (1≤t≤2*1e4).

The first line of each test case contains a single integer n (1≤n≤2*1e5) — number of chapters.

Then nn lines follow. The ii-th line begins with an integer ki(0≤ki≤n−1) — number of chapters required to understand the ii-th chapter. Then kiki integers ai,1,ai,2,…,ai,ki(1≤ai,j≤n,ai,j≠i,ai,j≠ai,l,for j≠l) follow — the chapters required to understand the i-th chapter.

It is guaranteed that the sum of nn and sum of kiki over all testcases do not exceed 2*1e5.

Output

For each test case, if the entire book can be understood, print how many times you will read it, otherwise print −1.

题意:给你n本书,在读第i本书之前,你必须先读x本书。而且可以进行一系列的操作,每次操作是从第一本到第n本书,去阅读你目前可以读的书。求最少的操作数使得你能够阅读完所有书籍。

解析:此题可以变为一个有向图。就是一个拓扑排序的模板,但是唯一的不同是,它要记录每个点取出来时候的轮数。最后的答案就是轮数的最大值。而如果是个环就输出-1。这题有个细节就是我们要用优先队列来保证小的数先取出来。

#include<bits/stdc++.h>
#include<queue>
#include<vector>
using namespace std;
int in[200005];
pair<int,int> t;
int Max;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
vector<int>edge[200005];
vector<int>ans;
void topology_sort() {
	while(!q.empty()) {
		Max=max(Max,q.top().first);
		t=q.top();
		ans.push_back(t.second);
		q.pop();
		for(int i=0; i<edge[t.second].size(); ++i) {
			in[edge[t.second][i]]--;
			if(in[edge[t.second][i]]==0) {
				if(edge[t.second][i]>t.second) {
					q.push(make_pair(t.first,edge[t.second][i]));
				} else q.push(make_pair(t.first+1,edge[t.second][i]));
			}
		}
	}
}
int main( ) {
	int t,x,u,n;
	cin>>t;
	while(t--) {
		ans.clear();
		for(int i=1;i<=20000;++i){
			edge[i].clear();
		}
		memset(in,0,sizeof(in));
		Max=0;
		cin>>n;
		for(int i=1; i<=n; ++i) {
			cin>>x;
			for(int j=1; j<=x; ++j) {
				cin>>u;
				edge[u].push_back(i);
				in[i]++;
			}
		}
		for(int i=1; i<=n; ++i) {
			if(in[i]==0) {
				q.push(make_pair(1,i));
			}
		}
		topology_sort();
		if(ans.size()!=n) cout<<-1<<endl;
		else cout<<Max<<endl;
	}
	return 0;
}

拓扑排序(入门)就这样结束啦。难的拓扑排序还会跟tarjan算法结合,跟dp结合(TAT)。希望以后这些学完,再来讲讲。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拓扑排序是一种对有向无环图进行排序的算法,它可以用来确定前驱关系,验证图中是否有环等。拓扑排序的基本思想是将图中的节点按照一定的顺序排列,使得所有的有向边从排在前面的节点指向排在后面的节点。在实际应用中,拓扑排序常常被用来解决任务调度、编译顺序等问题。 在C++中,可以使用DFS算法进行拓扑排序。具体实现方法是,对于每个节点,先将其标记为正在访问中,然后遍历其所有的邻居节点,如果邻居节点已经被访问过了,就说明存在环,返回false;如果邻居节点还没有被访问过,就递归地对其进行访问。当所有的邻居节点都被访问完毕后,将该节点标记为已经访问过,并将其加入到拓扑序列中。最后,将拓扑序列倒序输出即可。 下面是一个使用DFS算法进行拓扑排序C++代码示例: ``` const int maxn = 100; int c[maxn]; int topo[maxn], t, n; int G[maxn][maxn]; bool dfs(int u) { c[u] = -1; for(int v = 0; v < n; v++) { if(G[u][v]) { if(c[v] < 0) return false; else if(!c[v] && !dfs(v)) return false; } } c[u] = 1; topo[--t] = u; return true; } bool topoSort() { t = n; memset(c,0,sizeof(c)); for(int u = 0; u < n; u++){ if(!c[u] && !dfs(u)) return false; } return true; } int main() { char ch[100] = {'a', 'b', 'c', 'd'}; n = 4; G[0][1] = 1; G[2][1] = 1; G[3][2] = 1; if(!topoSort()){ printf("无法拓扑排序\n"); return 0; } for(int i = 0; i < 4; i++){ printf("%c ", ch[topo[i]]); } printf("\n"); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值