思想:

做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间。初始时dfn[i]=low[i]

在DFS过程中会形成一搜索树。在搜索树上越先遍历到的节点,显然dfn的值就越小。

DFS过程中,碰到哪个节点,就将哪个节点入栈。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。

如果发现某节点u有边连到搜索树中栈里的节点v,则更新u的low 值为dfn[v](更新为low[v]也可以)。
如果一个节点u已经DFS访问结束,而且此时其low值等于dfn值,则说明u可达的所有节点,都不能到达任何在u之前被DFS访问的节点
---- 那么该节点u就是一个强连通分量在DFS搜索树中的根。

此时将栈中所有节点弹出,包括u,就找到了一个强连通分量

以poj 1236 Network of Schools 为例说明

192K

  
0MS
#include <string.h>
#include <stdio.h>
#define V   
105
#define E   
100500

struct edge
{
    int to,
next;
}Edge[E];
int head[V], e, n;

int indeg[V], outdeg[V]; //点的入度和出度数
int belong[V], low[V], dfn[V], scc, cnt;//dfn[]:遍历到u点的时间;
low[]:u点可到达的各点中最小的dfn[v]
int S[V], top;
bool vis[V];//v是否在栈中

int addedge(int u, int v)
{
    Edge[e].to =
v;
    Edge[e].next
= head[u];
    head[u] =
e++;
    return
0;
}
void tarjan(int u)
{
    int v;
    dfn[u] =
low[u] = ++cnt;//开始时dfn[u] == low[u]
    S[top++] =
u;//不管三七二十一进栈
    vis[u] =
true;
    for (int
i=head[u]; i!=-1; i=Edge[i].next)
    {
   
    v =
Edge[i].to;
   
    if (dfn[v]
== 0)//如果v点还未遍历
   
    {
   
   
   
tarjan(v);//向下遍历
   
   
    low[u] =
low[u] < low[v] ? low[u] : low[v];//确保low[u]最小
   
    }
   
    else if
(vis[v] && low[u] >
dfn[v])//v在栈中,修改low[u]
   
   
    low[u] =
dfn[v];
    }
    if (dfn[u]
== low[u])//u为该强连通分量中遍历所成树的根
    {
   
    ++scc;
   
    do
   
    {
   
   
    v =
S[--top];//栈中所有到u的点都属于该强连通分量,退栈
   
   
    vis[v] =
false;
   
   
    belong[v] =
scc;
   
    } while (u
!= v);
    }

}

int solve()
{
    scc = top =
cnt = 0;
    memset(dfn,
0, sizeof(dfn));
    memset(vis,
false, sizeof(vis));
    for (int
u=1; u<=n; ++u)
   
    if (dfn[u]
== 0)
   
   
   
tarjan(u);
    return
scc;
}

void count_deg()
{
   
memset(indeg, 0, sizeof(indeg));
   
memset(outdeg, 0, sizeof(outdeg));
    for (int
u=1; u<=n; ++u)
   
    for (int
i=head[u]; i!=-1; i=Edge[i].next)
   
    {
   
   
    int v =
Edge[i].to;
   
   
    if
(belong[u] != belong[v])
   
   
    {
   
   
   
   
indeg[belong[v]]++;
   
   
   
   
outdeg[belong[u]]++;
   
   
    }
   
    }
}

int main()
{
    int u, v,
i;
    while
(~scanf("%d", &n))
    {
   
    e = 0;
   
    memset(head,
-1, sizeof(head));
   
    for (u=1;
u<=n; ++u)
   
   
    while
(scanf("%d", &v) &&
v != 0)
   
   
   
    addedge(u,
v);
   
   
solve();
   
    if (scc ==
1)
   
   
   
printf("1\n0\n");
   
    else
   
    {
   
   
   
count_deg();
   
   
    int inc = 0,
outc = 0;
   
   
    for (i=1;
i<=scc; ++i)
   
   
    {
   
   
   
    if (indeg[i]
== 0)
   
   
   
   
    inc++;
   
   
   
    if
(outdeg[i] == 0)
   
   
   
   
   
outc++;
   
   
    }
   
   
   
printf("%d\n%d\n", inc, (inc > outc ? inc :
outc));
   
    }
    }
    return
0;
}

poj1236 Tarjan算法模板 详解的更多相关文章

  1. Floyd算法模板--详解

    对于无权的图来说: 若从一顶点到另一顶点存在着一条路径,则称该路径长度为该路径上所经过的边的数目,它等于该路径上的顶点数减1. 由于从一顶点到另一顶点可能存在着多条路径,每条路径上所经过的边数可能不同 ...

  2. 一致性算法RAFT详解

    原帖地址:http://www.solinx.co/archives/415?utm_source=tuicool&utm_medium=referral一致性算法Raft详解背景 熟悉或了解 ...

  3. 各大公司广泛使用的在线学习算法FTRL详解

    各大公司广泛使用的在线学习算法FTRL详解 现在做在线学习和CTR常常会用到逻辑回归( Logistic Regression),而传统的批量(batch)算法无法有效地处理超大规模的数据集和在线数据 ...

  4. C++模板详解

    参考:C++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板:   仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...

  5. 转】Mahout推荐算法API详解

    原博文出自于: http://blog.fens.me/mahout-recommendation-api/ 感谢! Posted: Oct 21, 2013 Tags: itemCFknnMahou ...

  6. MD5算法步骤详解

    转自MD5算法步骤详解 之前要写一个MD5程序,但是从网络上看到的资料基本上一样,只是讲了一个大概.经过我自己的实践,我决定写一个心得,给需要实现MD5,但又不要求很高深的编程知识的童鞋参考.不多说了 ...

  7. 25.C++- 泛型编程之函数模板(详解)

    本章学习: 1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数和函数模板 当我们想写个Swap()交换函数时,通常这样写: void Swap(int& a, int&am ...

  8. 26.C++- 泛型编程之类模板(详解)

    在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板   类模板介绍 和函数模板一样,将泛型思想应用于类. 编译器对类模板处理方式和函数模板相同,都是进行2次编译 类模板通 ...

  9. [转]Mahout推荐算法API详解

    Mahout推荐算法API详解 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeepe ...

随机推荐

  1. sessionStorage 详解,特点,使用技巧,场景

    很早之前久知道sessionStorage ,也学习过,但没有实战使用过 .最近团队遇到一个问题<electronjs中打开新页面sessionStorage丢失>,让我有机会重新来认识一 ...

  2. UVA439 knightMoves (A*启发搜索)

    第一个A*,纪念下. A*要保证最短路一定要估价函数小于等于实际值,越接近越好 估价函数取Manhattan距离除以二. //Rey #include<cstdio> #include&l ...

  3. python处理图片的一些操作

    1.把图片分割成一个个竖条: from PIL import Image gap = 20 img_name = '/home/sensetime/000132_11_4.png' im = Imag ...

  4. Java中的集合Collection接口

    /* 集合:集合是存储对象数据的集合容器.集合比数组的优势: 1. 集合可以存储任意类型的对象数据,数组只能存储同一种数据类型 的数据. 2. 集合的长度是会发生变化的,数组的长度是固定的.----- ...

  5. Dapper学习总结

    看了<Dapper从入门到精通>后的总结 (1)Dapper 是直接扩展 IDBConnection,而且是单独一个文件,可以直接嵌入到项目中使用. (2)通过手写sql语句,调用exec ...

  6. Bootstrap 页面标题(Page Header)

    Bootstrap页面标题(PageHeader)是个不错功能,它会网页的标题的四周添加适当的间距,当一个网页中有多个标题并且每个标题之间需要添加一定适当的间距,使用页面标题是非常有用的.如果需要使用 ...

  7. 【上下界网络流 二分】bzoj2406: 矩阵

    感觉考试碰到上下界网络流也还是写不来啊 Description Input 第一行两个数n.m,表示矩阵的大小. 接下来n行,每行m列,描述矩阵A. 最后一行两个数L,R. Output 第一行,输出 ...

  8. hibernate简介以及简单配置

    Hibernate简介: Hibernate是一个开源对象关联关系映射的框架,他对JDBC做了轻量级的封装,使我们可以通过面向对象的思想操作数据库. 为什么要用Hibernate: 1: 对JDBC访 ...

  9. 服务器TIME_WAIT和CLOSE_WAIT分析和解决办法

    先上两张图: 查看TIME_WAIT和CLOSE_WAIT数的命令: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a ...

  10. MySQL如何复制一个表

    MySQL如何复制一个表 1 复制 employee 表 => employee2 () create table employee2 like employee () insert into ...