图的连通性Tarjan(有向图)
前置知识
- 强联通图:强连通图(Strongly Connected Graph)是指在有向图G中,如果对于任意两点u, v,从u到v和从v到u都有路径,则称G是强连通图
- 极大强连通子图:一个有向图中无法再加一个点的强连通子图
- 强连通分量(scc):一个有向图的极大强连通子图
- 领接表存图
- dfs
解决问题
例题:
1.[HAOI2006]受欢迎的牛
1 //题意为求一个图中唯一的出度为0的强联通 2 3 #include <bits/stdc++.h> 4 using namespace std; 5 const int MAXN = 100001; 6 const int MAXM = 50001; 7 int n, m; 8 9 int head[MAXN], cnt, nxt[MAXM], v[MAXM]; 10 void add(int x, int y) { 11 nxt[++cnt] = head[x]; 12 head[x] = cnt; 13 v[cnt] = y; 14 } 15 16 int dfn[MAXN], low[MAXN], ind;//dfn[u] : dfs图过程中搜到u的顺序 ,low[u] : u和u可到的子树中dfn的最小值 17 int s[MAXN], stp;//栈 18 int scccnt, sccnum[MAXN], sccsize[MAXN]; 19 20 int out[MAXN];//每个强联通的出度 21 int ans = 0; 22 23 void tarjan(int now) { 24 dfn[now] = low[now] = ++ind; 25 s[stp++] = now;//stp存的是栈顶的上一个位置 26 27 for (int i = head[now]; i ; i = nxt[i]) { 28 29 if(!dfn[v[i]]) { 30 tarjan(v[i]); 31 low[now] = min(low[v[i]], low[now]);//树枝边 32 } 33 else if(!sccnum[v[i]]) { 34 low[now] = min(dfn[v[i]], low[now]);//前后向边 35 } 36 37 } 38 39 40 if(dfn[now] == low[now]) { // 分割点 41 scccnt++; 42 while(s[stp] != now) { 43 sccnum[s[--stp]] = scccnt; 44 sccsize[scccnt]++; 45 } 46 } 47 48 return; 49 } 50 51 int main() { 52 53 scanf("%d%d", &n, &m); 54 for (int i = 1; i <= m; i++) { 55 int x, y; 56 scanf("%d%d", &x, &y); 57 add(x, y); 58 } 59 60 for (int i = 1; i <= n; i++) { 61 if(!dfn[i]) tarjan(i); 62 } 63 //for (int i = 1; i <= n; i++) printf("dfn[%d] = %d, low[%d] = %d, sccnum[%d] = %d\n", i, dfn[i], i, low[i], i, sccnum[i]); 64 65 //求唯一的出度为0的强联通 66 for (int i = 1; i <= n; i++) { 67 for (int j = head[i]; j ; j = nxt[j]) { 68 if(sccnum[i] != sccnum[v[j]]) 69 out[sccnum[i]]++; 70 } 71 } 72 bool flag = 0; 73 for (int i = 1; i <= scccnt; i++) { 74 if(flag == 1 && out[i] == 0) { 75 printf("0"); 76 return 0; 77 } 78 else if(out[i] == 0) { 79 ans = sccsize[i]; 80 flag = 1; 81 } 82 } 83 84 85 printf("%d", ans); 86 87 88 return 0; 89 }