图论复习之强连通分量以及缩点—Tarjan算法

by RtPYH

------------------------------------------------------------------------------------------------

【强连通分量以及连通子图】

#define#

在一个图的子图中,任意两个点相互可达,也就是存在互通的路径,那么这个子图就是强连通分量。(如果一个有向图的任意两个点相互可达,那么这个图就称为强连通图)。

【性质】

如果u是某个强连通分量的根,那么:

(1)u不存在路径可以返回到它的祖先。

(2)u的子树也不存在路径可以返回到u的祖先。

【算法描述】

(1)我们先对每一个顶点判断,如果已经被运行过了,则不动,否则进行拓展

(2)对于每一个点,我们设2个值来表示(dfn和low){low[i]表示结点i的根结点的dfn值},dfn则是结点i的时间戳

(3)在第一次顺序遍历时,dfn[u]=low[u]=++cnt,初始化序列

(4)然后将遍历到的点压入栈,并且置为已做过

(5)在整个图中枚举一条以u为右端点的边(from,to),然后判断左端点是否已经入栈,如果已经入栈,则修改low[u]:low[u]=max(low[u],dfn(to)),如果没入栈,则对其进行递归处理,返回时low[u]=min(low[u],low[to])

(6)当出现dfn[u]==low[u]时,则表明已经出现一个强联通分量,依次弹出并消除标记即可,此时cnt++,则该作用为统计联通块数量

【总结】

上述算法时间复杂度O(N+M)

【例题】

codevs2822《爱在心中》

题目描述 Description

“每个人都拥有一个梦,即使彼此不相同,能够与你分享,无论失败成功都会感动。爱因为在心中,平凡而不平庸,世界就像迷宫,却又让我们此刻相逢Our Home。”

在爱的国度里有N个人,在他们的心中都有着一个爱的名单,上面记载着他所爱的人(不会出现自爱的情况)。爱是具有传递性的,即如果A爱B,B爱C,则A也爱C。

如果有这样一部分人,他们彼此都相爱,则他们就超越了一切的限制,用集体的爱化身成为一个爱心天使。

现在,我们想知道在这个爱的国度里会出现多少爱心天使。而且,如果某个爱心天使被其他所有人或爱心天使所爱则请输出这个爱心天使是由哪些人构成的,否则输出-1。

输入描述 Input Description

第1行,两个数N、M,代表爱的国度里有N个人,爱的关系有M条。

第2到第M+1行,每行两个数A、B,代表A爱B。

输出描述 Output Description

第1行,一个数,代表爱的国度里有多少爱心天使。

第2行,如果某个爱心天使被其他所有人和爱心天使所爱则请输出这个爱心天使是由哪些人构成的(从小到大排序),否则输出-1。

样例输入 Sample Input

样例输入1:

6 7

1 2

2 3

3 2

4 2

4 5

5 6

6 4

样例输入2:

3 3

1 2

2 1

2 3

样例输出 Sample Output

样例输出1:

2

2 3

样例输出2:

1

-1

很明显,对于题目的第一问,我们只需要将给出的关系图跑一遍裸的tarjan即可

然而第二问显然就不是那么简单了,题目要求我们找出容量为n-1的联通块,这里要注意:

不能直接计算,因为你的图已经被第一问毁了。所以要再次构图

然后统计每个点的“爸爸”以及每个点的强联通分量个数

再寻找为n的即可,然后依次输出

时间复杂度O(4N+M)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MaxN=1001;

int s[MaxN],top;
int n,m;
int ans;//联通块个数
int dfn[MaxN],low[MaxN];
bool vis[MaxN],inq[MaxN];
int belong[MaxN];
int head[MaxN],hav[MaxN],h[MaxN];

struct graph{
	int to,next;
}G[MaxN],R[MaxN];

int cnt1=0,cnt2=0;

inline int minx(int a,int b){return a<b?a:b;}

void addedge(int u,int v){
	G[++cnt1].to=v;
	G[cnt1].next=head[u];
	head[u]=cnt1;
}

void tarjan(int u){
	dfn[u]=low[u]=++cnt2;
	s[++top]=u;
	vis[u]=inq[u]=true;
	for(int i=head[u];i!=0;i=G[i].next){
	  	if(!dfn[G[i].to]){
	  	  tarjan(G[i].to);
	  	  low[u]=minx(low[u],low[G[i].to]);
	    }
	    else
	    	if(inq[G[i].to])
			  low[u]=minx(low[u],dfn[G[i].to]);
	  }
	if(dfn[u]==low[u]){
		ans++;
		int j=0;
		while(j!=u){
			j=s[top--];
			inq[j]=false;
			belong[j]=ans;
			++hav[ans];
		}
	}
}

void rebuild(){
	int i,j,cnt=0;
	for(i=1;i<=n;i++)
		for(j=head[i];j!=0;j=G[j].next)
	      if(belong[G[j].to]!=belong[i])
	    	{
			  R[++cnt].to=belong[G[j].to];
			  R[cnt].next=h[belong[i]];
			  h[belong[i]]=cnt;
		    }
}
void work(){
  int i,ans1=0;
  for(i=1;i<=n;i++){
  	if(!vis[i])
  	  tarjan(i);
  }
  rebuild();
  for(i=1;i<=ans;i++)
    if(hav[i]>1)ans1++;
  printf("%d\n",ans1);
  ans1=-1;
  for(i=1;i<=ans;i++)
    if(!h[i]){
    	if(ans1!=-1 || hav[i]==1)
    	  {ans1=-1;break;}
    	else
    	  ans1=i;
    }
    if(ans1==-1)printf("%d",ans1);
    else {
    	for(i=1;i<=n;i++)
    	  if(belong[i]==ans1)printf("%d ",i);
    }
}

int main(){
  scanf("%d %d",&n,&m);
  int i,u,v;
  memset(vis,false,sizeof(vis));
  for(i=1;i<=m;i++){
  	scanf("%d %d",&u,&v);
  	addedge(u,v);
  }
  work();
  return 0;
}

-----------------------------------------thanks for watching------------------------------------------

时间: 2024-10-18 11:13:29

图论复习之强连通分量以及缩点—Tarjan算法的相关文章

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

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

【转】BYV--有向图强连通分量的Tarjan算法

转自beyond the void 的博客: https://www.byvoid.com/zhs/blog/scc-tarjan 注:红色为标注部分 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶

POJ 3352 Road Construction(边双连通分量,桥,tarjan)

题解转自http://blog.csdn.net/lyy289065406/article/details/6762370   文中部分思路或定义模糊,重写的红色部分为修改过的. 大致题意: 某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,保证任意2个旅游景点之间有路径连通的(可间接连通).而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施. 道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行.然而有道路部门正在施工的道路,在施工完毕前是

tarjan算法(强连通分量 + 强连通分量缩点 + 桥 + 割点 + LCA)

这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量,缩点,桥,割点,LCA等,日后写到相应的模板题我就会放上来. 1.强连通分量(分量中是任意两点间都可以互相到达) 按照深度优先遍历的方式遍历这张图. 遍历当前节点所出的所有边.在遍历过程中: ( 1 ) 如果当前边的终点还没有访问过,访问. 回溯回来之后比较当前节点的low值和终点的low值.将较小

【原创】tarjan算法初步(强连通子图缩点)

tarjan算法的思路不是一般的绕!!(不过既然是求强连通子图这样的回路也就可以稍微原谅了..) 但是研究tarjan之前总得知道强连通分量是什么吧.. 上百度查查: 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.有向图的极大强连通子图,称为强连通分量(strongly connected co

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

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

poj1236 Network of Schools ,求强连通分量(Tarjan算法),缩点

题目链接: 点击打开链接 题意: 给定一个有向图,求: 1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点 2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点 顶点数<= 100 求完强连通分量后,缩点,计算每个点的入度,出度. 第一问的答案就是入度为零的点的个数, 第二问就是max(n,m) // 入度为零的个数为n, 出度为零的个数为m. //kuangbin巨巨分析很棒! #include<cstdio> #include<cstring>

tarjan算法+缩点--cojs 908. 校园网

cojs 908. 校园网 ★★   输入文件:schlnet.in   输出文件:schlnet.out   简单对比时间限制:1 s   内存限制:128 MB USACO/schlnet(译 by Felicia Crazy) 描述 一些学校连入一个电脑网络.那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”).注意如果 B 在 A 学校的分发列表中,那么 A 不必也在 B 学校的列表中. 你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受

HDU ACM 1827 Summer Holiday-&gt;强连通分量+缩点(tarjan算法)

分析:首先求强连通分量的个数,然后进行缩点,最后求出最终答案. 1.求强连通分量的个数使用tarjan算法. 2.缩点为另外一个图,通过tarjan算法求出的结果进行.缩点后的图中求出每个点的入度. 3.求出每个强连通分量中的最小花费. 4.根据缩点后图的入度求出最终结果. #include<iostream> #include<vector> #include<stack> using namespace std; vector<int> map[1002