在Tarjan算法的过程中维护一个栈,并按如下方法维护其中的元素
1:当一个节点第一次被访问时,入栈。
2:当割点判定法则中dfn[x]<=Low[y]成立时
无论X是否为根,都要
1:从栈顶不断顶出节点,直到节点Y被弹出
2:刚才弹出的所有节点与节点X一起构成一个V-DCC
注意节点X还在栈中
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N = 20010, M = 200010; int head[N], ver[M], Next[M]; int dfn[N], low[N], stack[N], new_id[N], c[N], belong[M]; int d[N], dist[N], f[N][16]; int n, m, t, tot, num, root, top, cnt, tc; bool cut[N]; vector<int> dcc[N]; int hc[N], vc[M], nc[M]; queue<int> q; void add(int x, int y) { ver[++tot] = y, Next[tot] = head[x], head[x] = tot; } void add_c(int x, int y) { vc[++tc] = y, nc[tc] = hc[x], hc[x] = tc; } void tarjan(int x) { dfn[x] = low[x] = ++num; stack[++top] = x; if (x == root && head[x] == 0) { dcc[++cnt].push_back(x); return; } int flag = 0; for (int i = head[x]; i; i = Next[i]) { int y = ver[i]; if (!dfn[y]) { tarjan(y); low[x] = min(low[x], low[y]); if (low[y] >= dfn[x]) //x-->y,发现low[y]>=dfn[x],则X是一个割点 { flag++; if (x != root || flag > 1) //如果X不为根,或者X为根,但有两个子树时 cut[x] = true; cnt++; int z; do //将栈中的元素不断弹出来,直到Y这个结点 { z = stack[top--]; dcc[cnt].push_back(z); } while (z != y); dcc[cnt].push_back(x);//将X这个点也加入点双中,但X仍在栈中 } } else low[x] = min(low[x], dfn[y]); } } int main() { cin>>n>>m; memset(head, 0, sizeof(head)); memset(hc, 0, sizeof(hc)); memset(dfn, 0, sizeof(dfn)); memset(d, 0, sizeof(d)); memset(cut, 0, sizeof(cut)); memset(c, 0, sizeof(c)); for (int i = 1; i <= n; i++) dcc[i].clear(); tot = 1; num = cnt = top = 0; for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); add(x, y), add(y, x); } for (int i = 1; i <= n; i++) if (!dfn[i]) root = i, tarjan(i); for (int i=1;i<=cnt;i++) { cout<<"e-dcc "<<i<<endl; for (int j=0;j<dcc[i].size();j++) cout<<dcc[i][j]<<" "; cout<<endl; } } /* 4 4 1 2 2 4 2 3 3 4 */
运行结果
e-dcc 1
4 3 2
e-dcc 2
2 1