Tarjan 求强连通分量模板、参考博客
#include<stdio.h> #include<stack> #include<algorithm> using namespace std; const int maxn = 1e3 + 10; const int maxm = 330000 + 10; struct EDGE{ int v, nxt; }Edge[maxm]; int Head[maxn], cnt; int DFN[maxn], LOW[maxn], color[maxn], INDEX, id; bool vis[maxn]; int N, M; stack<int> stk; inline void init() { while(!stk.empty()) stk.pop(); for(int i=0; i<=N; i++) Head[i] = DFN[i] = LOW[i] = color[i] = -1, cnt = INDEX = id = 0; } inline void AddEdge(int from, int to) { Edge[cnt].v = to; Edge[cnt].nxt = Head[from]; Head[from] = cnt++; } inline void tarjan(int u) { DFN[u] = LOW[u] = INDEX++; stk.push(u); vis[u] = true; for(int i=Head[u]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(DFN[Eiv] == -1){ tarjan(Eiv); LOW[u] = min(LOW[u], LOW[Eiv]); }else{ if(vis[Eiv]) LOW[u] = min(LOW[u], LOW[Eiv]); } } if(DFN[u] == LOW[u]){ color[u] = ++id; vis[u] = false; while(stk.top() != u){ vis[stk.top()] = false; color[stk.top()] = id; stk.pop(); } stk.pop(); } } int main(void) { while(~scanf("%d %d", &N, &M)){ init(); int from, to; while(M--){ scanf("%d %d", &from, &to); AddEdge(from, to); } for(int i=0; i<N; i++) if(DFN[i] == -1) tarjan(i); printf("%d ", id); } return 0; }
Tarjan 求桥和割点模板
#include<bits/stdc++.h> using namespace std; const int maxn = 1e3 + 10;///图中顶点的数量 const int maxm = 4e5 + 10;///图中边的数量 struct EDGE{ int v, nxt; }Edge[maxm]; int Head[maxn], cnt;///表头以及边的编号 int LOW[maxn], DFN[maxn];///每个点最早可回溯到的祖先节点、每个点的遍历序号 int Fa[maxn], INDEX;///Fa数组记录每一个点的父亲、INDEX是算法里的时间戳 int N, M; inline void init() { for(int i=0; i<=N; i++) Head[i] = LOW[i] = DFN[i] = -1, Fa[i] = 0; cnt = INDEX = 0; } inline void AddEdge(int from, int to) { Edge[cnt].v = to; Edge[cnt].nxt = Head[from]; Head[from] = cnt++; } void Tarjan(int v, int Father) { Fa[v] = Father; DFN[v] = LOW[v] = INDEX++; for(int i=Head[v]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(DFN[Eiv] == -1){ Tarjan(Eiv, v); LOW[v] = min(LOW[v], LOW[Eiv]); } else if(Father != Eiv) LOW[v] = min(LOW[v], DFN[Eiv]); } } ///这份代码中顶点编号是从 0 ~ N-1 void Count()///统计割点和桥 { Tarjan(0, -1); int Cut_Num = 0;///割点的数量 int Root_Child = 0;///根节点的儿子 Tarjan(0, -1); for(int i=1; i<N; i++){ int v = Fa[i]; if(v == 0) Root_Child++; else if(LOW[i] >= DFN[v] && !is_cut[v]) is_cut[v] = true, Cut_Num++; } if(Root_Child > 1) is_cut[0] = true, Cut_Num++;///根节点有超过一个儿子则说明也是割点 for(int i=0; i<N; i++){ int v = Fa[i]; if(v >= 0 && LOW[i] > DFN[v]){ // v->i is bridge //可以用一个 pair<int, int> 来记录 } } } int main(void) { while(~scanf("%d %d", &N, &M)){ init(); int from, to; while(M--){ scanf("%d %d", &from, &to); AddEdge(from, to); AddEdge(to, from); } Count(); } return 0; }