// 此博文为迁移而来,写于2015年4月14日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vxnx.html

UPDATE(20180809):对代码和描述进行大量修改。

UPDATE(20151104):新增Tarjan算法核心代码。

1、前言
       我始终记得去年冬天有天吃完饭后,我们在买东西的时候讨论着强连通分量和Tarjan什么的。当时我真的什么都没听懂啊。。。什么强连通图,强连通分量,极大强连通分量。。。当然现在还是知道了。
       
2、概念
       Tarjan算法,由Tarjan发明。作用在于求图中的强连通分量。什么是强连通?在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。          
       先举一个很简单的例子,下图中,子图{1,2,3,4}中的节点两两可达,所以子图{1,2,3,4}为其强连通分量。
 

求强连通分量的话,除了枚举什么的,还有两种O(N+M)的方法,Kosaraju算法或Tarjan算法。今天介绍Tarjan算法。
 
3、求强连通分量
       由于我们跑的肯定是有向图,所以其实我们可以把每个强连通分量看成一棵子树。首先要定义两个数组:dfn(u)为节点u搜索的次序编号(时间戳,并不是深度),low(u)为u或u的子树能够追溯到的最早的栈中节点的次序编号。于是存在一个定理(也是最核心的判断方法):当dfn(u)=low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。Tarjan是一个图上深度优先搜索的算法,下面为以上图为样本的具体操作步骤:
 
(1)第一次递归,将1,2,5,6全部加入栈中。到终点节点6时,发现dfn[6]=low[6]=4,即{6}是一个强连通分量。
       (UPDATE:图中有一处错误,low[5]=3而不是1,特此说明)
(2)回溯后,同样可以得到dfn[5]=low[5],即(5}是一个强连通分量。(图略)
 
(3)再次回溯,由节点2搜索到3,将3加入栈中,发现3存在一条子边连向已经在栈中的节点1,如紫色边所示,则可得到low[3]=dfn[1]=1。另一条边连向节点6,但是6没有在栈中,则不管了。。

(4)返回节点2之后,我们可得low[2]=low[3]=1。(图略)
(5)返回节点1之后,搜索到节点4,存在一条边与在栈中的3相连,则low[4]=dfn[3]=5。至此,搜索结束,返回节点1,发现dfn[1]=low[1]=1,则在栈中的{1,2,3,4}组成一个强连通分量。Tarjan算法结束。

综上所述,求得的三个强连通分量为:{1,2,3,4},{5},{6}。
 
代码:
#include <cstdio>

#define MAXN 1005
#define MAXM 10005 int o, n, m, u, v, h[MAXN], ins[MAXN], dfn[MAXN], low[MAXN], tim, st[MAXN << ], t, tot, top[MAXN], ans[MAXN][MAXN]; struct Edge {
int v, next;
} e[MAXM]; int min(int a, int b) {
return a < b ? a : b;
} void add(int u, int v) {
o++, e[o] = (Edge) {v, h[u]}, h[u] = o;
} void tarjan(int o) {
dfn[o] = low[o] = ++tim, ins[o] = , st[++t] = o;
for (int x = h[o]; x; x = e[x].next) {
int v = e[x].v;
if (!dfn[v]) tarjan(v), low[o] = min(low[o], low[v]);
else if (ins[v] && dfn[v] < low[o]) low[o] = dfn[v];
}
if (dfn[o] == low[o]) {
int x; tot++;
do {
x = st[t--];
ins[x] = ;
ans[tot][++top[tot]] = x;
} while (x != o);
}
} int main() {
freopen("tarjan.in", "r", stdin);
freopen("tarjan.out", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = ; i <= m; i++) scanf("%d %d", &u, &v), add(u, v);
for (int i = ; i <= n; i++) if (!dfn[i]) tarjan(i);
for (int i = ; i <= tot; i++) {
for (int j = ; j <= top[i]; j++) printf("%d ", ans[i][j]);
printf("\n");
}
return ;
}
 
4、缩点
       其实上面,Tarjan算法本身已经讲完,但是,强连通分量求了肯定不是用来玩的。后面会给出一道许运用到Tarjan+缩点的题目,现在先讲概念。缩点的意思很简单,将一个强连通分量缩成一个点。作用不言而喻:如果题目所给的图存在环,还可以走重复的路,同时又要你求出权值和最大,怎么办?将强连通分量缩成点,接下来的任务就很简单了。在进行Tarjan求强连通分量的时候,我们就可以提前处理好一些内容,如得出缩点后的节点数,以及每个节点所属的强连通分量。
       缩点的过程有两种方式,根据情况可以选择:
  (1)双图法
    缩点后的节点全部重新存在新的图中。该方法的空间需求较大,但是一点都不麻烦。
  (2)新节点法
    如果原图存在n个节点,求出了k个非一个节点的强连通分量,则新加k个节点,共(n+k)个节点。在处理第i个强连通分量的时候,将所有与i中节点相连的边连到新的节点(n+i),同时对强连通分量中的节点全部进行标记(对边标记也可以),下次搜索的时候不可进行访问。
5、例题
 
抢掠计划  [APIO 2009]
       S城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 S 银行的 ATM 取款缩机。令人奇怪的是,S 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。 B 计划实施 S 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆祝他的胜利。 使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希 望你帮助他计计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个路口,道路的连接情况去网上找吧。
 
市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,B 能抢劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。 
 
输入格式 
第一行包含两个整数 n、m。n 表示路口的个数,m 表示道路条数。接下来 m 行,每行两个整数,这两个整数都在 1 到 n 之间,第 i+1 行的两个整数表示第 i 条道路的起点和终点的路口编号。接下来 n 行,每行一个整数,按顺序表示每 个路口处的 ATM 机中的钱数。接下来一行包含两个整数 s、p,s 表示市中心的 编号,也就是出发的路口。p表示酒吧数目。接下来的一行中有 p 个整数,表示 p 个有酒吧的路口的编号。 
 
输出格式 
输出一个整数,表示 B 从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。 
 
数据范围 
50%的输入保证 n, m<=3000。所有的输入保证n, m<=500000。每个 ATM 机中可取的钱数为一个非负整数且不超过 4000。输入数据保证你可以从市中心沿着 s 的单向的道路到达其中的至少一个酒吧。 
 
输入样例 
6 7 
1 2 
2 3 
3 5 
2 4 
4 1 
2 6 
6 5 
10 12 8 16 1 5 
1 4 
4 3 5 6 
 
输出样例 
47 
 
       主要到图中没有边权而有点权,且要求点权综合越大越好。就样例而言,出现的环对结果没有影响,但搜索的时候很难处理,延伸到所有强连通分量其实均可看为一点,故可使用Tarjan算法求出所有强连通分量,并进行缩点。这里采用的是上述的双图法,直接将所求的的所有强连通分量放进一个新图中。转化时注意一些细节即可。
  但由于本题数据较大,n/m <= 500000,爆栈感觉是件轻而易举的事情。这里仅提供较朴素的Tarjan+搜索算法,得分率为80%,存在TLE * 1和RE * 2。
#include <cstdio>

#define MAXN 500005
#define MAXM 500005 int n, m, u[MAXN], v[MAXN], tw[MAXN], ts, p, to, tb[MAXN], th[MAXN];
int w[MAXN], b[MAXN], s, h[MAXN], o, lik[MAXN];
int dfn[MAXN], low[MAXN], ins[MAXN], st[MAXN], tot, t, tim;
int f[MAXN], ans; struct Edge {
int v, next;
} e[MAXM], te[MAXM]; int min(int a, int b) {
return a < b ? a : b;
} int max(int a, int b) {
return a > b ? a : b;
} void add(int u, int v, int t) {
if (t) to++, te[to] = (Edge) {v, th[u]}, th[u] = to;
else o++, e[o] = (Edge) {v, h[u]}, h[u] = o;
} void init() {
scanf("%d %d", &n, &m);
for (int i = ; i <= m; i++) scanf("%d %d", &u[i], &v[i]), add(u[i], v[i], );
for (int i = ; i <= n; i++) scanf("%d", &tw[i]);
scanf("%d %d", &ts, &p);
for (int i = ; i <= p; i++) scanf("%d", &o), tb[o] = ;
} void tarjan(int o) {
dfn[o] = low[o] = ++tim, ins[o] = , st[++t] = o;
for (int x = th[o]; x; x = te[x].next) {
int v = te[x].v;
if (!dfn[v]) tarjan(v), low[o] = min(low[v], low[o]);
else if (ins[v] && dfn[v] < low[o]) low[o] = dfn[v];
}
if (dfn[o] == low[o]) {
int x; tot++;
do x = st[t--], ins[x] = , lik[x] = tot; while (x != o);
}
} void rebuild() {
for (int i = ; i <= m; i++) if (lik[u[i]] != lik[v[i]]) add(lik[u[i]], lik[v[i]], );
for (int i = ; i <= n; i++) {
w[lik[i]] += tw[i];
if (i == ts) s = lik[i];
if (tb[i]) b[lik[i]] = ;
}
f[s] = w[s];
} void DFS(int o) {
if (b[o]) ans = max(ans, f[o]);
for (int x = h[o]; x; x = e[x].next) {
int v = e[x].v;
if (f[o] + w[v] > f[v]) f[v] = f[o] + w[v], DFS(v);
}
} int main() {
init();
for (int i = ; i <= n; i++) if (!dfn[i]) tarjan(i);
rebuild();
DFS(s);
printf("%d", ans);
return ;
}

[知识点]Tarjan算法的更多相关文章

  1. tarjan算法 POJ3177-Redundant Paths

    参考资料传送门 http://blog.csdn.net/lyy289065406/article/details/6762370 http://blog.csdn.net/lyy289065406/ ...

  2. 有向图强连通分量的Tarjan算法

    有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G ...

  3. 点/边 双连通分量---Tarjan算法

    运用Tarjan算法,求解图的点/边双连通分量. 1.点双连通分量[块] 割点可以存在多个块中,每个块包含当前节点u,分量以边的形式输出比较有意义. typedef struct{ //栈结点结构 保 ...

  4. 割点和桥---Tarjan算法

    使用Tarjan算法求解图的割点和桥. 1.割点 主要的算法结构就是DFS,一个点是割点,当且仅当以下两种情况:         (1)该节点是根节点,且有两棵以上的子树;         (2)该节 ...

  5. Tarjan算法---强联通分量

    1.基础知识 在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子 ...

  6. (转载)LCA问题的Tarjan算法

    转载自:Click Here LCA问题(Lowest Common Ancestors,最近公共祖先问题),是指给定一棵有根树T,给出若干个查询LCA(u, v)(通常查询数量较大),每次求树T中两 ...

  7. 强连通分量的Tarjan算法

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

  8. Tarjan 算法&模板

    Tarjan 算法 一.算法简介 Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度. 我们定义: 如果两个顶点可以相互通达,则称两个顶点强连 ...

  9. 【小白入门向】tarjan算法+codevs1332上白泽慧音 题解报告

    一.[前言]关于tarjan tarjan算法是由Robert Tarjan提出的求解有向图强连通分量的算法. 那么问题来了找蓝翔!(划掉)什么是强连通分量? 我们定义:如果两个顶点互相连通(即存在A ...

随机推荐

  1. 基于SNMP的路由拓扑发现算法收集

    一.三层(网络层)发现 算法来源:王娟娟.基于SNMP的网络拓扑发现算法研究.武汉科技大学硕士学位论文,2008 数据结构: 待检路由设备网关链表:存放指定深度内待检路由设备的网关信息,处理后删除. ...

  2. 在asp.net利用jquery.MultiFile实现多文件上传(转载)

    转载地址:http://www.cnblogs.com/scy251147/archive/2010/09/30/1839313.html 官网链接:http://www.fyneworks.com/ ...

  3. 设计模式学习之适配器模式(Adapter,结构型模式)(14)

    参考链接:http://www.cnblogs.com/zhili/p/AdapterPattern.html一.定义:将一个类的接口转换成客户希望的另一个接口.Adapter模式使得原本由于接口不兼 ...

  4. 自制工具:迅速打开一个Node 环境的Playground

    需求 经常有这种情况,写代码的时候需要实验种想法,亟需一种playground 环境来玩耍.如果是前端的话可以打开chrome 的控制台,但是如果是Node 的话就比较麻烦了.我要打开我的存放试验代码 ...

  5. AspectFill VS. AspectFit

    从去年10月进入公司,到现在差不多忙碌了3个月,期间几乎所有精力和时间都花在了公司的项目上,有很多工作学习的心得一直没有总结,趁周末无事就来使这写一写. 除了刚进公司的那一个月是做一些修修补补的工作, ...

  6. php+jquery+ajax实现用户名验证

    大多数情况下,jquery代码的编写,都要求我们将jquery的代码放在以下三种中任一个function里. 有三种写法,同样效果,有点像Window.onload,但也有不同,就是window.on ...

  7. 关于phpcms v9投票模块选项排序listorder设定问题

    关于phpcms v9投票模块选项排序listorder设定问题修改,主要修改了三个文件三处地方. 主要修改三个文件: .phpcms\modules\vote\templates\vote_edit ...

  8. git warning: LF will be replaced by CRLF in...

    如果你有git项目,在提交代码的过程中可能会碰到上面的警告,特别是的项目中包含序列化对象的时候,你可能要小心!! 警告的含义是说换行符的,不同的操作系统的换行符是不一致的,如果你不清楚,真得看看这个 ...

  9. java net编程

    转自:http://www.cnblogs.com/linzheng/archive/2011/01/23/1942328.html 一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台 ...

  10. a与a:link、a:visited、a:hover、a:active

    原文地址http://www.cnblogs.com/exmyth/p/3226654.html a与a:link.a:visited.a:hover.a:active   起因: a与a:link的 ...