SCC(强连通分量)
1.定义:
在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(SC---strongly connected)。
有向图中的极大强连通子图,成为强连通分量(SCC---strongly connected components)。
下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达,{5},{6}也分别是两个强连通分量。

tarjan算法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值,Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量。
- 数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。
- 堆栈:每搜索到一个点,将它压入栈顶。
- 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。
- 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。
- 每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。
- 继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
///其时间复杂度也是O(N+M)
#include <stdio.h>
#include <string.h>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
#define N 10005 /// 题目中可能的最大点数
stack<int>sta; /// 存储已遍历的结点
vector<int>gra[N]; /// 邻接表表示图
int dfn[N]; /// 深度优先搜索访问次序
int low[N]; /// 能追溯到的最早的次序
int InStack[N];
/// 检查是否在栈中(2:在栈中,1:已访问,且不在栈中,0:不在)
vector<int> Component[N]; /// 获得强连通分量结果
int InComponent[N]; /// 记录每个点在第几号强连通分量里
int Index,ComponentNumber;/// 索引号,强连通分量个数
int n, m; /// 点数,边数 void init()///清空容器,数组
{
memset(dfn, , sizeof(dfn));
memset(low, , sizeof(low));
memset(InStack, , sizeof(InStack));
Index = ComponentNumber = ;
for (int i = ; i <= n; ++ i)
{
gra[i].clear();
Component[i].clear();
}
while(!sta.empty())
sta.pop();
} void tarjan(int u)
{
InStack[u] = ;
low[u] = dfn[u] = ++ Index;
sta.push(u);///寻找u所在的强连通分量
for (int i = ; i < gra[u].size(); ++ i)
{
int t = gra[u][i];
if (dfn[t] == )///不在的继续递归
{
tarjan(t);///递归到头了就
low[u] = min(low[u], low[t]);
}
else if (InStack[t] == )///在栈里
{
low[u] = min(low[u], dfn[t]);
}
}
if(low[u] == dfn[u])///sta出栈就是一个强连通分量的
{
++ComponentNumber;///强连通分量个数
while (!sta.empty())
{
int j = sta.top();
sta.pop();
InStack[j] = ;///已访问但不在栈中
Component[ComponentNumber].push_back(j);
///用vector存储第ComponentNumber个强连通分量
InComponent[j]=ComponentNumber;
///记录每个点在第几号强连通分量里
if (j == u)
break;
}
}
} void input(void)
{
for(int i=; i<=m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
gra[a].push_back(b);///有向图才有强连通分量
}
} void solve(void)
{
for(int i=; i<=n; i++)
if(!dfn[i])
tarjan(i);
if(ComponentNumber > )
puts("No");
else
puts("Yes");
} int main()
{
while(scanf("%d%d",&n,&m),n+m)
{
init();
input();
solve();
/*每一个强连通分量的具体数字
for(int i = 1; i <= ComponentNumber; i++)
{
for(int j = 0; j < Component[i].size(); j++)
if(!j)
cout << Component[i][j];
else
cout <<"-->"<< Component[i][j];
cout<<endl;
}
*/
}
return ;
}
HDU1269也能做模板
///时间复杂度为O(n+m)
///调用
///1、init()
///2、把图用add 存下来,注意图点标为1-n,若是[0,n-1]则给所有点++;
///3、调用tarjan_init(n); 再调用suodian();
///4、新图就是vector<int>G[]; 新图点标从1-tar ;
///5、对于原图中的每个点u,都属于新图中的一个新点Belong[u];
///新图一定是森林。
///6、新图中的点u 所表示的环对应原图中的vector<int> bcc[u];
///7、旧图中u在新图中所属的点是Belong[u];
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <iostream>
#include <algorithm>
#define repu(i, a, b) for(int i = a; i < b; i++)
using namespace std;
#define N 30100
///N为最大点数
#define M 150100
///M为最大边数
int n, m;///n m 为点数和边数 struct Edge
{
int from, to, nex;
bool sign;///是否为桥
} edge[M<<]; int head[N], edgenum; void add(int u, int v) ///边的起点和终点
{
Edge E = {u, v, head[u], false};
edge[edgenum] = E;
head[u] = edgenum++;
} int DFN[N], Low[N], Stack[N], top, Time;
///Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;///连通分支标号,从1开始
int Belong[N];///Belong[i] 表示i点属于的连通分支
bool Instack[N];
vector<int> bcc[N]; ///标号从1开始 void tarjan(int u ,int fa)
{
DFN[u] = Low[u] = ++ Time ;
Stack[top ++ ] = u ;
Instack[u] = ;
for (int i = head[u] ; ~i ; i = edge[i].nex )
{
int v = edge[i].to ;
if(DFN[v] == -)
{
tarjan(v , u) ;
Low[u] = min(Low[u] ,Low[v]) ;
if(DFN[u] < Low[v])
edge[i].sign = ;///为割桥
}
else if(Instack[v])
Low[u] = min(Low[u] ,DFN[v]) ;
}
if(Low[u] == DFN[u])///找到一个强连通分量
{
int now;
taj ++ ;
bcc[taj].clear();
do
{
now = Stack[-- top] ;
Instack[now] = ;
Belong [now] = taj ;
bcc[taj].push_back(now);///存储一个强连通分量
}
while(now != u) ;
}
} void tarjan_init(int all)
{
memset(DFN, -, sizeof(DFN));
memset(Instack, , sizeof(Instack));
memset(Low, , sizeof(Low));
top = Time = taj = ;
for(int i=; i<=all; i++)
if(DFN[i] == - )
tarjan(i, i); ///注意开始点标!!!
} vector<int>G[N];
int du[N];
void suodian()
{
memset(du, , sizeof(du));
for(int i = ; i <= taj; i++)
G[i].clear();
for(int i = ; i < edgenum; i++)
{
int u = Belong[edge[i].from], v = Belong[edge[i].to];
if(u!=v)
G[u].push_back(v), du[v]++;
}
} void init()
{
memset(head, -, sizeof(head));
edgenum=;
} int main()
{
while(scanf("%d%d",&n,&m) && (n + m))
{
init();
repu(i,,m)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
tarjan_init(n);
// repu(i,1,taj + 1)///输出每一个强连通分量
// {
// for(int j = 0; j < bcc[i].size(); j++)
// cout<<bcc[i][j]<<" ";
// cout<<endl;
// }
if(taj > )
printf("No\n");
else
printf("Yes\n");
}
return ;
}
九野模板--HDU1269
参考链接http://blog.csdn.net/xinghongduo/article/details/6195337
SCC(强连通分量)的更多相关文章
- poj 1236 scc强连通分量
分析部分摘自:http://www.cnblogs.com/kuangbin/archive/2011/08/07/2130277.html 强连通分量缩点求入度为0的个数和出度为0的分量个数 题目大 ...
- 强连通分量的Tarjan算法
资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tar ...
- poj 2186 有向图强连通分量
奶牛互相之间有爱慕关系,找到被其它奶牛都喜欢的奶牛的数目 用tarjan缩点,然后判断有向图中出度为0的联通分量的个数,如果为1就输出联通分量中的点的数目,否则输出0. 算法源自kb模板 #inclu ...
- HDU1269 迷宫城堡(裸强连通分量)
Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A ...
- HDU 4635 Strongly connected (2013多校4 1004 有向图的强连通分量)
Strongly connected Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- POJ - 1236 Network of Schools(有向图的强连通分量)
d.各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输, 问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件. 问题2:至少需要添加几条传输线 ...
- HDU - 1269 迷宫城堡(有向图的强连通分量)
d.看一个图是不是强连通图 s.求出强连通分量,看看有没有一个强连通分量包含所有点. c.Tarjan /* Tarjan算法 复杂度O(N+M) */ #include<iostream> ...
- 【HDU5934】Bomb——有向图强连通分量+重建图
题目大意 二维平面上有 n 个爆炸桶,i−thi-thi−th爆炸桶位置为 (xi,yi)(x_i, y_i)(xi,yi) 爆炸范围为 rir_iri ,且需要 cic_ici 的价格引爆, ...
- 强连通分量SCC 2-SAT
强连通分量SCC 2-SAT 部分资料来自: 1.https://blog.csdn.net/whereisherofrom/article/details/79417926 2.https://ba ...
- 有向图强连通分量SCC(全网最好理解)
定义: 在有向图中,如果一些顶点中任意两个顶点都能互相到达(间接或直接),那么这些顶点就构成了一个强连通分量,如果一个顶点没有出度,即它不能到达其他任何顶点,那么该顶点自己就是一个强连通分量. 做题的 ...
随机推荐
- "Unity测试系列"文章索引
对Unity各种API的细节进行测试 Common 一些Unity基础操作的性能测试 Animation/Animator Animation Play/Stop测试 关于Animation动画事件的 ...
- C++ Programming language读书笔记
C语言,结构化程序设计.自顶向下.逐步求精及模块化的程序设计方法;使用三种基本控制结构构造程序,任何程序都可由顺序.选择.循环三种基本控制结构构造. 模块结构:"独立功能,单出.入口&quo ...
- EF获取一个或者多个字段
有时候直接查询出一个实体,比较浪费性能,对于字段比较少的表来说差异不大,但是如果一个表有几十个字段,你只要取出一个字段或者几个字段,而取出整个实体,性能就会有差异了. /// <summary& ...
- JAVA线程锁-读写锁
JAVA线程锁,除Lock的传统锁,又有两种特殊锁,叫读写锁ReadWriteLock 其中多个读锁不互斥,读锁和写锁互斥,写锁和写锁互斥 例子: /** * java线程锁分为读写锁 ReadWri ...
- excel如何用公式判断单元格的值是否为数字、英文、中文,以及相应的计数
一.excel如何用公式判断单元格的值是否为数字.英文.中文. A列为数据列,B列为判断列=LOOKUP(CODE(ASC(A1)),{48,65,123;"数字","英 ...
- 针对高通BMS的研究 高通电量计
点击打开链接 高通8064 8974 8926等pm芯片都集成了电量计,估计后续芯片都会一直存在,现在许多项目UI状态栏电池都有百分比显示,所以需要深入分析BMS有助于解决电量方面的BUG. 一: S ...
- java 导出Excel 大数据量,自己经验总结!(二)
在上一次的基础上加上了样式,以及中文列名 package com.tommy.fundation.util; import java.io.OutputStream; import java.util ...
- 《BI那点儿事》SSRS图表和仪表——雷达图分析三国超一流谋士、统帅数据(图文并茂)
雷达图分析三国超一流谋士.统帅数据,献给广大的三国爱好者们,希望喜欢三国的朋友一起讨论,加深对传奇三国时代的了解 建立数据环境: -- 抽取三国超一流谋士TOP 10数据 DECLARE @t1 TA ...
- java jar包解析:打包文件,引入文件
java jar包解析:打包文件,引入文件 cmd下: jar命令:package包打包 javac命令:普通类文件打包 Hello.java: package org.lxh.demo; publi ...
- android 5.0 水波纹 实现
1. 定义一个普通圆角背景的xml; rounded_corners.xml <?xml version="1.0" encoding="utf-8"?& ...