BDFZOI 边的分类

涉及知识:基础图论/DFS

提交次数:4

描述

给定无权有向图G(V,E),dfs确定每条边的种类。当边(u, v)第一次被遍历,

考虑v的颜色

1.白色,(u,v)为T边,包含在dfs树中

2.灰色,(u,v)为B边,dfs树中子孙指向自己直系祖先的一条边

3.黑色: (u,v)为F边或C边. 此时需要进一步判断

3.1. 若prev[u]<prev[v]:u是v的直系祖先,F边<prev[v]:u是v的直系祖先,f边

3.2. 若prev[u]>prev[v]:v早就被发现了, C边

输入

第一行包含两个整数N、M,表示该图共有N个结点和M条有向边。(N <= 5000,M <= 200000)
接下来M行,每行包含2个整数{u,v},表示有一条有向边 u指向v

输出

M行,依照输入顺序,每行输出边的u、v、种类

样例输入

5 9
1 2
1 3
1 4
2 3
3 4
3 5
5 1
5 2
5 4

样例输出

1 2 T
1 3 F
1 4 F
2 3 T
3 4 T
3 5 T
5 1 B
5 2 B
5 4 C

代码:
 1 #include<iostream>
 2 #include<vector>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 int n, m;
 7 struct edge{
 8     int u, e;
 9 };
10 vector<vector<edge> >v(5005);
11 int dfn[5005];
12 int finish[5005];
13 bool visited[5005];
14 int ttime;
15 
16 void dfs(int uu, int step){
17     dfn[uu] = ttime++;
18     for(int i = 0; i < v[uu].size(); i++){
19         int next = v[uu][i].u;
20         if(dfn[next]==-1) v[uu][i].e = 1;//T边 
21         else if (finish[next]==-1) v[uu][i].e = 2;//B边
22         else{
23             if(dfn[next]>dfn[uu]) v[uu][i].e = 3; //F边
24             else if (dfn[next]<dfn[uu]) v[uu][i].e = 4;//C边 
25         }
26         if(visited[next]==false){
27             visited[next] = true;
28             dfs(next, step+1);
29         }
30     }
31     finish[uu] = ttime++;
32 }
33 int main(){
34     cin>>n>>m;
35     int i, j;
36     for(i = 1; i <= m; i++){
37         edge ee;
38         int a, b;
39         cin>>a>>b;
40         ee.u = b;
41         ee.e = 0;
42         v[a].push_back(ee); 
43     }
44     memset(dfn,-1,sizeof(dfn));
45     memset(finish,-1,sizeof(finish));
46     visited[1] = true;    
47     dfs(1,0);
48     for(i = 1; i <= n; i++){
49         if(!visited[i]){
50             visited[i] = true;
51             dfs(i,0);
52         }
53     }
54     for(i = 1; i <= n; i++)
55         for(j = 0; j < v[i].size();j++){
56             switch(v[i][j].e){
57                 case 1:
58                     cout<<i<<" "<<v[i][j].u<<" "<<"T"<<endl;
59                     break;
60                 case 2:
61                     cout<<i<<" "<<v[i][j].u<<" "<<"B"<<endl;
62                     break;
63                 case 3:
64                     cout<<i<<" "<<v[i][j].u<<" "<<"F"<<endl;
65                     break;
66                 case 4:
67                     cout<<i<<" "<<v[i][j].u<<" "<<"C"<<endl;
68                     break;
69             }        
70         }
71         
72     return 0;
73 } 
备注:
这周上课时老师补充的内容,属于比较基础的内容。利用dfs树,设置两个时间戳dfn[i]为发现结点i的时间,finish[i]为离开这个点,
也就是出栈的时间戳。这样就可以将点染色,白色为未访问的结点,即dfn为-1;灰色为已发现,但未完成的结点;黑色则为已完成的结点,
即finish!=-1。于是也可以把边进行分类了,分为树边,前向边,后向边和叉边。具体见dfs内的几行代码。
T树边:灰->白
B后向边:灰->灰
F前向边:灰->黑
C叉边:灰->黑
无向图没有后两者。
还是犯了一些错误的。比如每个点都只能访问一次,对于已经访问过的点,只要判断一下边,不能接着dfs下去。
还有就是图不一定连通,所以每个点都要判断一下要不要dfs。另外得4分是因为忘了标记再次dfs的点。

原文地址:https://www.cnblogs.com/fangziyuan/p/7030301.html