<学习笔记> 拓扑排序
定义:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。(引自百度百科)
条件:有向无环图。
拓扑排序的步骤:
1.找出入度为0的点
2.删除这个点和这个点连出的所有边。
3.若已经没有入度为0的点,排序结束。
循环结束后,若输出的顶点数小于图中的顶点数,就说明存在着环。
由于拓扑排序可以保证一条边的起点一定比他的终点早出现,所以可以用于解决一些奇奇怪怪的问题和对一些算法进行优化(如差分、前缀处理)。
我们求拓扑排序通常会用到队列。
每次弹出对头元素,并把它的出边所连向的点入度--,如果入度为0,就压入队列,正确性显然。
例题 洛谷 3183 HAOI2016 食物链
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 using namespace std; 8 9 int N,M,a,b,cnt,Ans=0; 10 int first[100010],next[200010],rd[100010],ans[100010]; 11 int app[100010]; 12 13 struct maple{ 14 int f,t; 15 }Rode[200010]; 16 17 void Build(int f,int t) 18 { 19 Rode[++cnt]=(maple){f,t}; 20 next[cnt]=first[f]; 21 first[f]=cnt; 22 } 23 24 queue<int> q; 25 int main() 26 { 27 scanf("%d%d",&N,&M); 28 for(int i=1;i<=M;++i) 29 { 30 scanf("%d%d",&a,&b); 31 Build(a,b); 32 ++rd[b],++app[a],++app[b]; 33 } 34 for(int i=1;i<=N;++i) 35 if(!rd[i]&&app[i]) 36 { 37 ans[i]=1; 38 q.push(i); 39 } 40 while(!q.empty()) 41 { 42 int A=q.front(); 43 q.pop(); 44 int f=0; 45 for(int i=first[A];i;i=next[i]) 46 { 47 f=1; 48 --rd[Rode[i].t]; 49 ans[Rode[i].t]+=ans[A]; 50 if(!rd[Rode[i].t]) q.push(Rode[i].t); 51 } 52 if(!f) Ans+=ans[A]; 53 } 54 printf("%d",Ans); 55 return 0; 56 }