在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通。如果图中任意两个顶点之间都连通,则称该图为连通图,否则,称该图为非连通图,则其中的极大连通子图称为连通分量,这里所谓的极大是指子图中包含的顶点个数极大。
直观地说,极大就是不能再大,或者说再大也不能超过自己。因此,极大连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,且S是S'的子图,可推出S = S',
  则称S是G的极大连通子图。
  极小连通子图正好相反,极小就是不能再小,再多小一点就会不连通或点不足。因此,极小连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,S'包含G的所有顶点,且S'是S的子图,可推出S' = S,
  则称S是G的级小连通子图。
  注:这个定义和pinejeely给出的等价。这里给出的定义比较容易验证。
在有向图中,如果对于每一对顶点vi和vj,从vi到vj和从vj到vi都有路径,则称该图为强连通图;否则,将其中的极大强连通子图称为强连通分量。
 

Kosaraju算法:先dfs,得到最后完成时间f,再求反图,按f递减的方向对反图再dfs一次。

 #include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define N 1000
int head[N], headt[N], cnt;
struct node
{
int next, to;
}edge[N * ], edget[N * ]; void addedge(int from, int to)//G_T
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
//得到反图
edget[cnt].next = headt[to];
edget[cnt].to = from;
headt[to] = cnt;
}
int f[N];//finishtime
int d[N];//discovertime
int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int belong[N];//which scc
int cur;//current scc
void dfs_visit(int x)
{
color[x] = ;
d[x] = ++time;
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!color[j])
{
dfs_visit(j);
}
}
//color[x] = 2;
f[x] = ++time;
} void dfs_visit_t(int x)
{
color[x] = ;
//d[x] = ++time;
int i, j;
for (i = headt[x]; i; i = edget[i].next)
{
j = edget[i].to;
if (!color[j])
{
dfs_visit_t(j);
}
}
//color[x] = 2;
//f[x] = ++time;
belong[x] = cur;
} bool cmp(const int &a, const int &b)
{
return a > b;
} map<int, int, greater<int> > mp;//使用map对f[i]进行从大到小排序
map<int, int>::iterator it; void init()
{
cnt = cur = ;
time = -;
memset(head, , sizeof(head));
memset(f, , sizeof(f));
memset(d, , sizeof(d));
memset(color, , sizeof(color));
memset(belong, , sizeof(belong));
mp.clear();
} int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = ; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = ; i <= n; i++)//得到f
if (!color[i])
dfs_visit(i);
//sort(f, f + n, cmp);
for (i = ; i <=n; i++)//对f排序
mp[f[i]] = i;
memset(color, , sizeof(color));
for (it = mp.begin(); it != mp.end(); ++it)//对反图进行dfs
{
if (!color[it->second])
{
dfs_visit_t(it->second);
cur++;
}
}
for (i = ; i <=n; i++)
printf("%d ", belong[i]);
}
return ;
}

Tarjan算法只需一次dfs,而且不用求反图,dfs找最早祖先点(最先被发现)也就是寻找B边。

使用LOW函数测试此条件
 #include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#define INF 0x7fffffff
using namespace std;
#define N 1000 stack<int> s;
int head[N], cnt;
struct node
{
int next, to;
}edge[N * ]; void addedge(int from, int to)
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
//int f[N];//finishtime
int pre[N];//discovertime
//int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int id[N];//which scc
int low[N];//
int cur;//current scc
void dfs_visit(int x)
{
low[x] = pre[x] = ++time;
s.push(x);
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!pre[j])
{
dfs_visit(j);
}
if (low[j] < low[x])//找最小的low[x],即是否存在后向边(B边)
low[x] = low[j];
}
if (low[x] == pre[x])//找到了一个scc
{
do
{
i = s.top();
s.pop();
low[i] = INF;
id[i] = cur;
}while (i != x);
cur++;
}
} void init()
{
cnt = cur = ;
time = ;
memset(head, , sizeof(head));
memset(pre, , sizeof(pre));
memset(low, , sizeof(low));
memset(id, , sizeof(id));
while (!s.empty())
s.pop();
} int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = ; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = ; i <= n; i++)
if (!pre[i])
dfs_visit(i);
for (i = ; i <=n; i++)
printf("%d ", id[i]);
}
return ;
}

Gabow算法:思路与tarjan思路一样,但是用一个stack替代了low数组,使得交换次数减小

 #include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#define INF 0x7fffffff
using namespace std;
#define N 1000 stack<int> s;
stack<int> p;
int head[N], cnt;
struct node
{
int next, to;
}edge[N * ]; void addedge(int from, int to)
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
//int f[N];//finishtime
int pre[N];//discovertime
//int color[N];//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int id[N];//which scc
int low[N];//
int cur;//current scc
void dfs_visit(int x)
{
low[x] = pre[x] = ++time;
s.push(x);
p.push(x);
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!pre[j])
dfs_visit(j);
if (!id[j])//该点未在已求的scc中
while (pre[j] < pre[p.top()])存在后向边,出栈
p.pop();
}
if (p.top() == x)//找到一个scc
{
p.pop();
do
{
i = s.top();
id[i] = cur;
s.pop();
}while (i != x);
cur++;
}
} void init()
{
cnt = cur = ;
time = ;
memset(head, , sizeof(head));
memset(pre, , sizeof(pre));
memset(low, , sizeof(low));
memset(id, , sizeof(id));
while (!s.empty())
s.pop();
while (!p.empty())
p.pop();
} int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = ; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = ; i <= n; i++)
if (!pre[i])
dfs_visit(i);
for (i = ; i <=n; i++)
printf("%d ", id[i]);
}
return ;
}

有向图连通分量SCC的更多相关文章

  1. HDU 1269 -- 迷宫城堡【有向图求SCC的数目 &amp;&amp; 模板】

    迷宫城堡 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  2. [Tarjan系列] Tarjan算法与有向图的SCC

    前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...

  3. 洛谷 P2746 [USACO5.3]校园网Network of Schools (Tarjan,SCC缩点,DAG性质)

    P2746 [USACO5.3]校园网Network of Schools https://www.luogu.org/problem/P2746 题目描述 一些学校连入一个电脑网络.那些学校已订立了 ...

  4. [Tarjan系列] Tarjan算法求无向图的桥和割点

    RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...

  5. COGS 2396 2397 [HZOI 2015]有标号的强连通图计数

    题意:求n个点有向图其中SCC是一个的方案数 考虑求出若干个不连通的每个连通块都是SCC方案数然后再怎么做一做.(但是这里不能用Ln,因为推不出来) 设$f_n$为答案, $g_n$为n个点的有向图, ...

  6. 【洛谷P3275】糖果

    题目大意:维护 M 个差分约束关系,问是否可以满足所有约束,如果满足输出一组解.\(N<=1e5\) 题解:差分约束模型可以通过构建一张有向图来求解.是否满足所有约束可以利用 spfa 进行判断 ...

  7. POJ - 3177 Redundant Paths (边双连通缩点)

    题意:在一张图中最少可以添加几条边,使其中任意两点间都有两条不重复的路径(路径中任意一条边都不同). 分析:问题就是最少添加几条边,使其成为边双连通图.可以先将图中所有边双连通分量缩点,之后得到的就是 ...

  8. POJ - 2942 Knights of the Round Table (点双联通分量+二分图判定)

    题意:有N个人要参加会议,围圈而坐,需要举手表决,所以每次会议都必须是奇数个人参加.有M对人互相讨厌,他们的座位不能相邻.问有多少人任意一场会议都不能出席. 分析:给出的M条关系是讨厌,将每个人视作点 ...

  9. [Tarjan系列] 无向图e-DCC和v-DCC的缩点

    上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC. 那么这一篇就是e-DCC和v-DCC的应用之一:缩点. 先讲e-DCC的缩点. 我们把每一个e-DCC都看成一个节点,把所有桥边(x,y ...

随机推荐

  1. poj 1815 Friendship【最小割】

    网络流的题总是出各种奇怪的错啊--没写过邻接表版的dinic,然后bfs扫到t点不直接return 1就会TTTTTLE-- 题目中的操作是"去掉人",很容易想到拆点,套路一般是( ...

  2. WPF-按钮美化

    我们不多哔哔,先放图: 美化按钮背景: 当我们用系统默认的按钮风格似乎太老套,而且不太美观,某些情况下我们需要对按钮进行美化和重绘,只有这样才能满足我们的需要 按钮美化思维引导: 图中1 为控件Bor ...

  3. ————————C语言中快速排序方法——————————————

    在对浮点型排序是一定要用三木运算符(三目运算符内容下去自己看),因为如果也是用整形那样的减法的时候如果是两个十分相近的数字 可能返回一个小数(自己一会去试试),冉冉他cmp返回值是int(试试别的)因 ...

  4. quickpow || 快速幂

    洛谷例题 推荐自行脑补:百度百科 如果  ,那么 : 前言:快速幂就是快速算底数的n次幂.其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高. 拿题目样例 Input :2 1 ...

  5. Ubuntu下如何用命令运行deb安装包

    转载自 WindTaiL的博客 如果ubuntu要安装新软件,已有deb安装包(例如:iptux.deb),但是无法登录到桌面环境.那该怎么安装?答案是:使用dpkg命令. dpkg命令常用格式如下: ...

  6. String Mark Codeforces - 895D

    一看好像会做的样子,就去做了一下,结果 猝不及防地T掉了 赶紧查了一下,没有死循环,复杂度也是对的,无果,于是翻了题解 题解没看懂,但是找到了标程,然后发现我被卡常了... 而且好像当时还过了前10个 ...

  7. 题解报告:hdu 1075 What Are You Talking About

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1075 Problem Description Ignatius is so lucky that he ...

  8. spring入门笔记-(一)、spring boot HelloWorld

    什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...

  9. Snort里如何将一个tcpdump格式的二进制文件读取打印到屏幕上(图文详解)

    不多说,直接上干货! 关于tcpdump二进制格式,这个基本概念不说. 支持tcpdump二进制格式的嗅探器工具,这里我说两个:tcpdump或者ethereal. [root@datatest Se ...

  10. JDK集合框架--LinkedList

    上一篇讲了ArrayList,它有一个"孪生兄弟"--LinkedList,这两个集合类总是经常会被拿来比较,今天就分析一下LinkedList,然后总结一下这俩集合类的不同 首先 ...