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. Temporary ASP.NET 拒绝访问

    CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\dd813f66 ...

  2. Tomcat内存设置

    MyEclipse Tomcat的JDK菜单:“Optional Java VM arguments:”框中输入“有前置空格”   -Xms512m -Xmx512m -XX:PermSize=512 ...

  3. centos7 docker mysql56

    yum -y install docker docker pull centos docker run --name=mysqltmp -i -t centos /bin/bash yum -y in ...

  4. html5 drag and drop

    注:链接.图片默认是draggable的. mousemove在整个拖放的过程中不会被触发. dragStart设置: e.dataTransfer.effectAllowed = "mov ...

  5. 《BI项目笔记》用Excel2013连接和浏览OLAP多维数据集

    用Excel2013连接和浏览OLAP多维数据集

  6. 某篇ctr预估ppt的链接

    csdn上面有一篇ppt,但是下载分太贵了.里面东西看起来讲的还可以.看看能不能嵌入. http://download.csdn.net/detail/u012289698/9371461 <i ...

  7. Android Gradle 完整指南

    为什么需要学Gradle? Gradle 是 Android 现在主流的编译工具,虽然在Gradle 出现之前和之后都有对应更快的编译工具出现,但是 Gradle 的优势就在于它是亲儿子,Gradle ...

  8. Server asks us to fall back to SIMPLE auth, but this client is configured to only allow secure connections.

    我是在flume向hdfs 写(sink)数据时遇到的这个错误. Server (是指hdfs) asks us to fall back to SIMPLE auth, but this clien ...

  9. windows docker安装方式的比较小结

    稍微小结一下使用InstallDocker 和dockertoolbox的两种方式安装的docker(名称说明可能不妥,仅代表安装方式) InstallDocker  使用的是Microsoft Hy ...

  10. 登陆界面Login

    最终界面:   XMAL 代码: <Grid > <Grid.RowDefinitions> <RowDefinition /> <RowDefinition ...