ACwing848. 有向图的拓扑序列

题目

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

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

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

输入格式

第一行包含两个整数n和m

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

输出格式

共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。

否则输出-1。

数据范围

1n,m105

输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3

分析

拓扑排序,就是找入度为0的点入队列,弹出队列后将其出边连接点的入度减一。写法和BFS的模板差不多,因为都是对队列弹出结点的出边的结点操作。区别:队列弹出结点信息要记录,因为弹出结点的顺就是拓扑排序顺序。可以开一个数组记录弹出结点的信息,或者用数组模拟队列。

整体思路:

1.先找到所有入度为0 的结点入队列q

2.弹出队头元素q.front(),并记录这个结点

3.找到这个结点的所有出边的结点,使这些结点的入度减一,若入度减一后为0,入队列。

4.循环2,3步直到队列为空

代码

用 ans存放拓扑排序结果

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 
 7 const int N = 100010;
 8 int h[N],e[N],ne[N],idx;
 9 int n,m;
10 int d[N];//存放每个结点的入度
11 vector<int>ans;//记录拓扑排序结果
12 
13 void add(int a,int b){
14     e[idx] = b,ne[idx] = h[a],h[a] = idx++;
15 }
16 
17 void topsort(){
18     queue<int>q;
19     //寻找度为0的点
20     for(int i = 1;i <=n;i++){
21         if(d[i] == 0){
22             q.push(i);
23         }
24     }
25     
26     while(!q.empty()){
27         int t = q.front();ans.push_back(t);
28         q.pop();
29         //将该元素出边的结点入度减一
30         for(int i = h[t];i!=-1;i = ne[i]){
31             int j = e[i];
32             d[j]--; //删掉边,入度减一
33             if(d[j] == 0){
34                 q.push(j);
35             }
36         }
37     }
38     
39 }
40 
41 int main(){
42     scanf("%d%d",&n,&m);
43     memset(h,-1,sizeof(h));
44     for(int i = 0;i < m;i++){
45         int a,b;
46         scanf("%d%d",&a,&b);
47         add(a,b);
48         d[b]++; //b的入度加1
49     }
50     topsort();
51     if(ans.size()!=n){
52         cout<<"-1";
53     }else{
54         for(auto it:ans) cout<<it<<" ";
55     }
56     return 0;
57 }

用数组模拟队列,这样会保留弹出结点信息

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 const int N = 100010;
 7 int h[N],e[N],ne[N],idx;
 8 int n,m;
 9 int d[N];//存放每个结点的入度
10 int q[N]; //用数组模拟队列
11 
12 void add(int a,int b){
13     e[idx] = b,ne[idx] = h[a],h[a] = idx++;
14 }
15 
16 bool topsort(){
17     int hh = 0, tt = -1;  //定义对头,队尾
18     //寻找度为0的点
19     for(int i = 1;i <=n;i++){
20         if(d[i] == 0){
21             q[++tt] = i;  //入队
22         }
23     }
24     
25     while(hh <= tt){
26         int t = q[hh++];
27         
28         //将该元素出边的结点入度减一
29         for(int i = h[t];i!=-1;i = ne[i]){
30             int j = e[i];
31             d[j]--; //删掉边,入度减一
32             if(d[j] == 0){
33                 q[++tt] = j;
34             }
35         }
36     }
37     return tt == n-1; //是否队列为n个点,来判断是否存在拓扑序列
38     
39 }
40 
41 int main(){
42     scanf("%d%d",&n,&m);
43     memset(h,-1,sizeof(h));
44     for(int i = 0;i < m;i++){
45         int a,b;
46         scanf("%d%d",&a,&b);
47         add(a,b);
48         d[b]++; //b的入度加1
49     }
50    if(topsort()){
51        for(int i = 0;i < n;i++)
52         printf("%d ",q[i]);
53    }else{
54        printf("-1");
55    }
56     return 0;
57 }
原文地址:https://www.cnblogs.com/fresh-coder/p/14441860.html