如果图没有重边,那么一般的求割边tarjan算法是这么操作的。
dfs访问每一个点时,为这个点分配一个时间戳dfn[],根据访问次序的先后,时间戳从小到大
对于边(u,v)是不是割边,如果lowv > dfn[u],那么边(u,v)是割边,反之不是。 lowv表示的是从点v开始dfs,所能访问到的最小时间戳。
如果从点v开始dfs,访问到的时间戳<=dfn[u]那么说明v或者v的子树有一条连向u或者u祖先的边。
从点1开始dfs,dfn[1] = 1, 沿边访问到点2,然后从点2开始dfs,但是点2没有连回1的边,所以边(1,2)是割边
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 using namespace std; 5 const int N = 1000 + 10; 6 vector<int> g[N]; 7 int dfs_clock,pre[N]; 8 int cnt; 9 int dfs(int u, int fa) 10 { 11 int lowu = pre[u] = ++dfs_clock; 12 int i,v,lowv; 13 for(i=0; i<g[u].size(); ++i) 14 { 15 v = g[u][i]; 16 if(!pre[v]) 17 { 18 lowv = dfs(v,u); 19 lowu = min(lowu,lowv); 20 if(lowv > pre[u])//相对于割点,只把等号给去掉了 21 cnt++; 22 } 23 else if(v!=fa && pre[v]<lowu)//v!=fa,说明不能用反向边来更新 24 lowu = pre[v]; 25 } 26 return lowu; 27 } 28 int main() 29 { 30 int i,x,y; 31 int n,m; 32 scanf("%d%d",&n,&m); 33 for(i=0; i<m; ++i) 34 { 35 scanf("%d%d",&x,&y); 36 g[x].push_back(y); 37 g[y].push_back(x); 38 } 39 dfs(1,-1); 40 41 printf("%d\n",cnt); 42 }