AcWing848 有向图的拓扑序列

给定一个n个点m条边的有向图,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。

若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。

输入格式
第一行包含两个整数n和m

接下来m行,每行包含两个整数x和y,表示存在一条从点x到点y的有向边(x, y)。

输出格式
共一行,如果存在拓扑序列,则输出拓扑序列。

否则输出-1

数据范围

\(1≤n,m≤105\)

输入样例

3 3

1 2

2 3

1 3

输出样例

1 2 3

一道拓扑排序的模板题。拓扑排序的算法是,首先找到入度为0的点(所以需要一个数组to[]维护每个结点的入度),将它放入队列,然后依次枚举它的所有出边t->j,删掉它们之间的连接to[j]--,如果此时to[j]==0,那么将j入队,持续这个过程直到队列为空。

代码:

#include <iostream>
#include <queue>
#include <memory.h>
using namespace std;
const int N = 1e5+10;
int h[N], e[N], ne[N], idx;
int to[N], ans[N];
int n, m;
void add(int a, int b){
    e[idx] = b; ne[idx] = h[a]; h[a] = idx++;   //邻接表加入边的一般操作
}

bool toposort(){
    queue<int> q;
    int pp = 0;
    for(int i = 1; i <= n; ++i){     //首先找到入度为0的结点,将它加入队列和答案中
        if(to[i] == 0){
            q.push(i); 
            ans[pp++] = i;
            break;
            
        }
    }
    while(!q.empty()){
        int u = q.front(); q.pop();       //取出队首元素
        for(int i = h[u]; i != -1; i = ne[i]){  //对每个u的出边
            int v = e[i]; to[v]--;        //删掉u->v的连接
            if(to[v] == 0) {             //如果v的入度为0了,那么加入队列和答案中
                ans[pp++] = v;
                q.push(v);
            }
        }
    }
    return pp==n;
}

int main(){
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof(h));
    for(int i = 0; i < m; ++i){
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);       //将a->b的边加入
        to[b]++;         //更新入度信息
    } 
    if(toposort()){      //如果有拓扑序,打印出结果
        for(int i = 0; i < n; ++i) printf("%d ", ans[i]);
    }else 
        printf("-1");
}
posted @ 2019-11-05 22:33  patrolli  阅读(161)  评论(0编辑  收藏  举报