【图论训练】最小拓扑序
最小拓扑序
topsort.pas/c/cpp
【题目描述】
给一个有向无环图,求其字典序最小的拓扑序。
一个拓扑序被认为字典序{pi}最小当且仅当对于任何其他拓扑序{qi},均存在正整数k,使得对于所有i<k有pi=qi且pk<qk。
【输入】
输入第一行包含两个数n, m分别表示有向无环图的点数和边数。
接下来m行,每行两个数ai, bi,表示图中存在一条ai指向bi的有向边。
【输出】
输出n个数,每个数用空格隔开,表示该图的拓扑序。
【输入样例】
3 1
2 1
【输出样例】
2 1 3
【样例解释】
2, 1, 3
2, 3, 1
3, 2, 1
均为合法拓扑序,但是2, 1, 3为最小字典序的拓扑序。
【数据规模】
50%的数据满足:n<=1000
100%的数据满足:1<=n<=100000, 1<=m<=100000, 1<=ai, bi<=n
保证数据至少存在一种合法的拓扑序列
如果直接枚举与入度为0的点相连的点,时间复杂度就太高了(n*n),所以在进行排序的时候,我们用前向星和优先队列来优化,复杂度是(n+m)
【话说师兄的指针好难看懂……_(:зゝ∠)_】
代码如下
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; priority_queue<int, vector<int>, greater<int> > q;//优先输出小的数的优先队列 int n,m; int t=0; int head[100001]; struct node{ int to,next; }e[100001]; int father[100001];//记录入度 void add(int u,int v) { t++; e[t].to=v; e[t].next=head[u]; head[u]=t; } void read() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); father[b]++; add(a,b); } for(int i=1;i<=n;i++) { if(father[i]==0) { q.push(i); father[i]--; } } } void work() { while(!q.empty()) { int p=q.top(); q.pop(); printf("%d ",p); int p1=head[p]; while(p1!=0) { father[e[p1].to]--; if(father[e[p1].to]==0) q.push(e[p1].to); p1=e[p1].next; } } } int main() { freopen("topsort.in","r",stdin); freopen("topsort.out","w",stdout); read(); work(); return 0; }