Tarjan算法 有向图SCC

一、引言

强连通分量是指有向图的一个极大联通子图,强连通分量中任意两个点都存在一条路径可以直接或间接互相到达。特别地,有向图G中,若对于 V(G) 中任意两个不同的顶点 u 和 v,都存在从 u 到 v 以及从 v 到 u 的路径,则称 G 是强连通图。

有向图的极大强连通子图被称为是“强连通分量”,简记为SCC。

举个栗子。如右图,G1是强连通的,G2不是强连通的。因为G2中,点4不能到达点1。

求强连通分量的Tarjan算法,时间复杂度可以做到 O(n+m)。

二、强连通分量缩点

若将有向图中的强连通分量都缩成一个点,则原图会形成一个DAG(有向无环图)。举个栗子:

三、基本算法

一些定义

Tarjan算法是基于深搜的算法,每个强联通分量为搜索树种的一棵子树。搜索时,把当前搜索树中未处理的结点加入堆栈,回溯时就可以判断栈顶到栈中的结点是否为一个强联通分量。

为了描述方便,我们对图的边进行一些定义。一棵 DFS 树被构造出来后,考虑图中的非树边,对边定义如下:

前向边:祖先→儿子的边。

后向边:儿子→祖先的边。

横叉边:没有祖先、儿子关系的边(注意横叉边只会往 dfn 减小的方向链接。)

dfn[x]:记录结点x在DFS过程中被遍历到的次序号(时间戳)。可知在同一个DFS树的子树中,dfn[x]越小,则其越浅。

low[x]:记录结点x或x的子树能够追溯到的dfn最小的值(栈中标号的最小点)。即在DFS树中,此点以及其后代指出去的边,能返回到的最浅的点的时间戳。

low的计算过程

我们阔以模拟一下low的计算过程。

举个栗子。

dfn[x]为结点x在DFS过程中被遍历到的次序号,不用解释。low[x]为结点x或x的子树能够追溯到的dfn最小的值(在DFS树中,此点以及其后代指出去的边,能返回到的最浅的点的时间戳)。

从结点1开始遍历。当到搜索结点5的时候,结点5已经没有指出去的边了,就是说,它不能再指向更浅的时间戳了。它就指向自己,所以结点5的low值就是4。接下来搜索4号点、6号点。搜索到6号点后,6号点返回,找到了4号点。所以6号点能搜索到的最浅的时间戳就是5(即4号点的dfn值)。4号点不能往其他地方遍历了,所以它的low值就是5。同理,可推出其他结点的low值。

可得,当dfn[x]=low[x]时,以x为根的搜索子树上所有结点构成一个强联通分量。

证明:dfn表示x点被DFS到的时间,low表示x和x所以子树结点最多只有指向x点的边,而没有指向x的祖先的边了。显然,遍历过的结点从x出发又最终回到x形成了一个环,即x点与它的子孙结点构成了强联通分量。

四、代码实现

根据定义,Tarjan算法按照以下步骤计算“追溯值”:

1.当节点x第一次被访问时,把x入栈,初始化 low[x]=dfn[x]。

2.扫描从x出发的每条边(x,y)。

  • 若y没被访问过,则说明(x,y)是树枝边,递归访问y,从y回溯之后,令 low[x]=min(low[x],low[y])。
  • 若y被访问过并且y在栈中,则令 low[xmin(low[x],dfn[y])。

3.从x回溯之前,判断是否有 low[x]=dfn[x]。若成立,则不断从栈中弹出节点,直至x出栈。

(这个板子好奇怪,感觉会炸的亚子

缩点后重建图:上述代码中,ins[x]表示x这个点所属的SCC编号(我也不知道为什么要用这个变量名)。那么建图的话,只需要遍历原有的边,如果边所对应的两个节点所属的SCC编号不同,那么则添边,最后得到一个有向无环图。

五、例题

[HAOI2006]受欢迎的牛

题目链接

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

首先考虑求出所有的极大强连通分量,显然一个分量内的牛是相互“受欢迎”的。考虑将每个分量看作一个点,以原图中端点属于不同分量的点为边组成新的图G,那么显然G中是没有环的(否则与极大的条件矛盾),这样图必定存在至少一个点出度为0,如果有多个点出度为0,那么答案显然为0(这些点之间显然无法直接或间接存在“受欢迎”关系)。如果只有一个点出度为0,那么只要满足所有点都能到达它即可,否则答案为0。如果上述条件均满足,那么答案就是该点对应的原分量中的点数。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int N=1e4+5,M=5e4+5;
 5 int n,m,cnt,num,dfn[N],low[N],ins[N],st[N],top,x,y,v[N],ans,k[N];
 6 int hd[N],to[M],nxt[M];
 7 void add(int x,int y){
 8     to[++cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt;
 9 }
10 void tarjan(int x){
11     dfn[x]=low[x]=++num,st[++top]=x;
12     for(int i=hd[x];i;i=nxt[i]){
13         int y=to[i];
14         if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
15         else if(!ins[y]) low[x]=min(low[x],dfn[y]);
16     }
17     if(low[x]==dfn[x]){
18         ins[x]=++cnt,++k[cnt];
19         while(st[top]!=x) ++k[cnt],ins[st[top]]=cnt,--top;
20         --top;
21     }
22 }
23 signed main(){
24     //freopen(".in","r",stdin);
25     //freopen(".out","w",stdout);
26     scanf("%lld%lld",&n,&m);
27     for(int i=1;i<=m;i++){
28         scanf("%lld%lld",&x,&y);
29         add(y,x);
30     }
31     cnt=0,x=0;
32     for(int i=1;i<=n;i++)
33         if(!dfn[i]) tarjan(i);
34     for(int i=1;i<=n;i++)
35         for(int j=hd[i];j;j=nxt[j])
36             if(ins[i]!=ins[to[j]]) v[ins[to[j]]]++;
37     for(int i=1;i<=cnt;i++)
38         if(!v[i]) ans=k[i],x++;
39     if(x==1) printf("%lld\n",ans);
40     else puts("0");
41     return 0;
42 }

POJ1236 Network of Schools

题目链接

题目大意:给定一个有向图,N个点,求:

(1) 至少要选几个顶点, 才能做到从这些顶点出发, 可以到达全部顶点。
(2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部项点。

Tarjan算法求SCC,并缩点建图。

那么对于问题1,新的图中入度为0的点的点数即是答案。

对于问题2,答案为max (入度为0的点的点数,出度为0的点数),因为对于每个入度或出度为0的点,需要连一条边来解决,那么将出度为0的点连向入度为0的点是最优的。

此外,如果最后SCC只有1个,那么问题2的答案应该特判为0。

1 #include<bits/stdc++.h>
2 #define int long long
3 using namespace std;
4 signed main(){
5     //freopen(".in","r",stdin);
6     //freopen(".out","w",stdout);
7     puts("Dlstxdy,lkytsdy!");
8     return 0;
9 }

显然这个代码是过不了这道题的

(待更新,先占个坑欢迎纠错

原文地址:https://www.cnblogs.com/maoyiting/p/12592849.html

时间: 2024-08-15 02:54:33

Tarjan算法 有向图SCC的相关文章

『Tarjan算法 有向图的强连通分量』

有向图的强连通分量 定义:在有向图\(G\)中,如果两个顶点\(v_i,v_j\)间\((v_i>v_j)\)有一条从\(v_i\)到\(v_j\)的有向路径,同时还有一条从\(v_j\)到\(v_i\)的有向路径,则称两个顶点强连通(strongly connected).如果有向图\(G\)的每两个顶点都强连通,称\(G\)是一个强连通图.有向图的极大强连通子图,称为强连通分量(strongly connected components). 万能的Tarjan算法也可以帮助我们求解有向图的强

有向图强连通分支的Tarjan算法讲解 + HDU 1269 连通图 Tarjan 结题报告

题目很简单就拿着这道题简单说说 有向图强连通分支的Tarjan算法 有向图强连通分支的Tarjan算法伪代码如下:void Tarjan(u) {dfn[u]=low[u]=++index//进行DFS,每发现一个新的点就对这个点打上时间戳,所以先找到的点时间戳越早,dfn[U]表示最早发现u的时间,low[u]表示u能到达的最早的时间戳.stack.push(u)//将U压入栈中for each (u, v) in E {if (v is not visted)//如果V点没有经历过DFS,则

有向图强连通分量的Tarjan算法(转)

原文地址:有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强

有向图强连通分量的Tarjan算法

有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,

强连通分量的Tarjan算法

资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tarjan算法详解理解集合 ppt图解分析下载 强连通分量 强连通分量(strongly connected component)是图论中的概念.图论中,强连通图指每一个顶点皆可以经由该图上的边抵达其他的每一个点的有向图.意即对于此图上每一个点对(Va,Vb),皆存在路径Va→Vb以及Vb→Va.强连通

图之强连通、强连通图、强连通分量 Tarjan算法

强连通分量 简介 在阅读下列内容之前,请务必了解图论基础部分. 强连通的定义是:有向图 G 强连通是指,G 中任意两个结点连通. 强连通分量(Strongly Connected Components,SCC)的定义是:极大的强连通子图. 这里想要介绍的是如何来求强连通分量. Tarjan 算法 Robert E. Tarjan (1948~) 美国人. Tarjan 发明了很多算法结构.光 Tarjan 算法就有很多,比如求各种联通分量的 Tarjan 算法,求 LCA(Lowest Comm

tarjan 算法讲解(转)

转自:https://www.byvoid.com/blog/scc-tarjan/ 網誌 列表 標籤 項目 關於 聯繫 四月142009 作者:byvoid 閱讀: 158882 計算機科學 圖論 強連通分量 Tarjan 堆棧 有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(s

有向图的强连通分量(tarjan算法)

强连通分量 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为强连通分量(strongly connected components). 考虑强连通分量C,设其中第一个被发现的点为x,则,C中其他的点都是x的后代.我们希望在x访问完成时立即输出C(可以同时记录C,输出代表

[图论] 有向图强连通分量 (kosaraju算法,Tarjan算法)

记录自己的想法:在有向图中,如果一些顶点中任意两个顶点都能互相到达(间接或直接),那么这些顶点就构成了一个强连通分量,如果一个顶点没有出度,即它不能到达其他任何顶点,那么该顶点自己就是一个强连通分量.在用kosaraju算法和Tarjan算法求强连通分量的时候,就是给所有的顶点分组染色,同一种颜色的顶点在同一个强连通分量中,记录有多少种颜色(有多少个强联通分量),每个顶点属于哪种颜色(每个顶点在哪个强连通分量重).在同一个强连通分量中的所有顶点可以缩为一个顶点,然后根据缩点构造DAG(有向无环图