1.定义:

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(SC---strongly connected)。

有向图中的极大强连通子图,成为强连通分量(SCC---strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达,{5},{6}也分别是两个强连通分量。

2.tarjan算法-----九野讲解

tarjan算法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值,Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量。

  1. 数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。
  2. 堆栈:每搜索到一个点,将它压入栈顶。
  3. 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。
  4. 当点p有与点p’相连时,如果此时(时间 = dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。
  5. 每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。这些出栈的元素组成一个强连通分量。
  6. 继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
///其时间复杂度也是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(强连通分量)的更多相关文章

  1. poj 1236 scc强连通分量

    分析部分摘自:http://www.cnblogs.com/kuangbin/archive/2011/08/07/2130277.html 强连通分量缩点求入度为0的个数和出度为0的分量个数 题目大 ...

  2. 强连通分量的Tarjan算法

    资料参考 Tarjan算法寻找有向图的强连通分量 基于强联通的tarjan算法详解 有向图强连通分量的Tarjan算法 处理SCC(强连通分量问题)的Tarjan算法 强连通分量的三种算法分析 Tar ...

  3. poj 2186 有向图强连通分量

    奶牛互相之间有爱慕关系,找到被其它奶牛都喜欢的奶牛的数目 用tarjan缩点,然后判断有向图中出度为0的联通分量的个数,如果为1就输出联通分量中的点的数目,否则输出0. 算法源自kb模板 #inclu ...

  4. HDU1269 迷宫城堡(裸强连通分量)

    Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A ...

  5. HDU 4635 Strongly connected (2013多校4 1004 有向图的强连通分量)

    Strongly connected Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  6. POJ - 1236 Network of Schools(有向图的强连通分量)

    d.各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输, 问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件. 问题2:至少需要添加几条传输线 ...

  7. HDU - 1269 迷宫城堡(有向图的强连通分量)

    d.看一个图是不是强连通图 s.求出强连通分量,看看有没有一个强连通分量包含所有点. c.Tarjan /* Tarjan算法 复杂度O(N+M) */ #include<iostream> ...

  8. 【HDU5934】Bomb——有向图强连通分量+重建图

    题目大意 二维平面上有 n 个爆炸桶,i−thi-thi−th爆炸桶位置为 (xi,yi)(x_i, y_i)(xi​,yi​) 爆炸范围为 rir_iri​ ,且需要 cic_ici​ 的价格引爆, ...

  9. 强连通分量SCC 2-SAT

    强连通分量SCC 2-SAT 部分资料来自: 1.https://blog.csdn.net/whereisherofrom/article/details/79417926 2.https://ba ...

  10. 有向图强连通分量SCC(全网最好理解)

    定义: 在有向图中,如果一些顶点中任意两个顶点都能互相到达(间接或直接),那么这些顶点就构成了一个强连通分量,如果一个顶点没有出度,即它不能到达其他任何顶点,那么该顶点自己就是一个强连通分量. 做题的 ...

随机推荐

  1. # TypeScript 中如何确保 this 的正确性

    问题 在 TS 里面 this 关键字一开始让我这个写 C# 的十分惊讶,比如下面的一段代码,注意 initBinding 方法 class Company { id:number; /** * 在点 ...

  2. 如何使用Python调用AutoIt来实现Flash控件的上传功能

    先看一段代码 upload.au3(这个后缀autoit3的格式): ;等待出现title为数据采集-军课网-MozillaFirefox的浏览器窗口 WinWait("数据采集 - XX网 ...

  3. C中测试时间代码

  4. nodejs系列(一)安装和介绍

    一.安装nodejs http://www.nodejs.org/download/.进入release/选择想要安装的文件,win下安装选择mis和exe的比较方便,安装完毕重新打开cmd命令行,p ...

  5. Delphi TTable 组件

    TTable 是 TDataSet 的派生类,它是基于 BDE 数据库引擎的数据集组件,也是一个较简单的数据组件,可以直接从数据库中获取数据表的数据,只需设置连接的数据库属性(Database) 和所 ...

  6. havok之内存管理

    [现象记录] 1.往world和rb里都各自加入一个entityListener,当这个rb被remove掉之后, 会首先调用world里的listener的removecallback, 再调用rb ...

  7. Ubuntu 16.04.1下修改MySQL默认编码

    在Ubuntu 下配置 MySQL 的字符编码.安装完 MySQL 后,系统默认的字符编码是 latin1 ,输入的是中文,可是输出却是一堆乱码.现在要做的就是把 MySQL的默认字符编码设置为支持中 ...

  8. GIT ON WINDOWS

    https://help.github.com/articles/generating-an-ssh-key/

  9. 转-浅谈HTTP中Get与Post的区别

    Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE.URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP ...

  10. Java之美[从菜鸟到高手演变]之设计模式

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...