【图论训练】最小拓扑序

最小拓扑序

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;
}

 

posted @ 2015-10-26 15:44  Oranges  阅读(264)  评论(0编辑  收藏  举报