图论三:拓扑排序
一、基本知识
1、定义:拓扑排序是对有向无圈图的排序,如果存在一条从vi--vj的路径,那么排序过程中vj必定在vi之后。
2、排序条件:必须是有向图并且无圈;
3、拓扑排序的结果不唯一。
二、排序过程
1、存储图结构:图的边集用邻接表存储,用vector数组来代替更好;还要一个存储入度的数组,表示每个节点的入度;
一个队列在遍历图时存储图的每个节点(如果对序号有升序,降序需要可以用优先队列)。
2、找到入度为0点,然后入队
3、队列的头结点出队,遍历头结点的邻接节点,并且删除邻接点与头结点的边(就是入度-1)
4、如果这个节点比入度变为0,则入队。
5、重复2,3,4操作,直到队列为空,就是结束。
三、例题
(1)对结果序列有要求(字典序)
题目链接:http://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=2153
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<string> #include<vector> using namespace std; const int maxn = 500; vector <int> edge[maxn]; int in[maxn]={0},vis[maxn]={0},num=0; int main(void) { int i,j; char a,b,c; string str,ans=""; while(cin>>str) { a=str[0];b=str[1];c=str[2]; if(vis[a]==0) vis[a]=1,num++; if(vis[c]==0) vis[c]=1,num++; if(b=='>') in[c]++,edge[a].push_back(c); else in[a]++,edge[c].push_back(a); } priority_queue <int,vector <int>, greater <int> > q; for(i=30;i<=220;i++) if(vis[i]&&in[i]==0) q.push(i); while(!q.empty()) { int top=q.top(); q.pop(); ans+=(char)(top); for(i=0;i<edge[top].size();i++) { in[edge[top][i]]--; if(in[edge[top][i]]==0) q.push(edge[top][i]); } } if(ans.size()!=num) cout<<"No Answer!"<<endl; else cout<<ans<<endl; return 0; }
(2)貌似字典序要求(比较坑)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4857
思路:约束条件:“a必须在b之前”,“让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推。”,所依按照拓扑排序建立的图,最先出队的节点不能确定是否先输出,而最后的可以:
eg:
图为:
3->6->5->1
2->8->7->1
4->9->10->1
如果正序建图:2 8 7 3 6 5 4 9 10 1
但正确结果是:3 6 5 2 8 7 4 9 10 1
可以确定前面小的不一定在前面,但是后面大的一定在后面,所以可以逆序建图,然后逆序输出。
参考文章:https://blog.csdn.net/qq_41713256/article/details/80805338
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn = 30030; int in[maxn],n,m; vector <int> edge[maxn],ans; int main(void) { int t,i,x,y; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) edge[i].clear(),in[i]=0; ans.clear(); for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); in[x]++;edge[y].push_back(x); } priority_queue <int> q; for(i=1;i<=n;i++) if(in[i]==0) q.push(i); while(!q.empty()) { int top=q.top(); ans.push_back(top); q.pop(); for(i=0;i<edge[top].size();i++) { in[edge[top][i]]--; if(in[edge[top][i]]==0) q.push(edge[top][i]); } } for(i=ans.size()-1;i>=0;i--) { if(i!=ans.size()-1) printf(" "); printf("%d",ans[i]); } printf("\n"); } return 0; }