这里的Tarjan是基于DFS,用于求有向图的强联通分量。

运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量。

§有向图中, u可达v不一定意味着v可达u.
   相互可达则属于同一个强连通分量
   (Strongly Connected Component, SCC)
§有向图和它的转置的强连通分量相同
§所有SCC构成一个DAG(有向无环图)

dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间

low[u]为u或u的子树能够追溯到的最早的栈中节点的次序号,即它所在的某个强联通分量(可能是自己一个点,也可能是一棵树)对应的搜索子树的根的访问时间

不难知道,当dfn(u)=low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

伪代码如下

tarjan(u)
{
DFN[u]=Low[u]=++time // 为节点u设定次序编号和Low初值
Stack.push(u) // 将节点u压入栈中
for each (u, v) in E // 枚举以u为起点的每一条边
if (v is not visted) // 如果节点v未被访问过
tarjan(v) // 继续向下找
Low[u] = min(Low[u], Low[v])
else if (v in S) // 如果节点v还在栈内,则形成一个强联通分量,low值为根的dfn
Low[u] = min(Low[u], DFN[v])//注意到环可能不止一个,有可能是环套环,所以我们的环是要最大的,也就是访问该子树根的时间要尽可能的早
if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根
repeat
v = S.pop // 将v退栈,为该强连通分量中一个顶点
print v
until (u==v)
}

至于缩点,我们可以强行重新建一个图,但有时我们只需要新图的一些性质,所以有时我们可以不需要重新建图,只统计我们需要的数据就可以了。一个例子可以看下面的例题。

POJ2186 Popular Cows

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当明星。

输入输出格式

输入格式:
第一行:两个用空格分开的整数:N和M

第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

输出格式:
第一行:单独一个整数,表示明星奶牛的数量

输入输出样例

输入样例#1:
3 3
1 2
2 1
2 3
输出样例#1:
1
说明

只有 3 号奶牛可以做明星

【数据范围】

10%的数据N<=20, M<=50

30%的数据N<=1000,M<=20000

70%的数据N<=5000,M<=50000

100%的数据N<=10000,M<=50000

 牛的相互喜欢构成了一个有向图,题目则要求我们求出一个点,这个点是其他所有点都可以到达的。

对于形成环的点,他们之间能相互到达,所以我们可以把它们缩成一个点。

这样我们通过tarjan缩点之后就形成了一个DAG,分析可以知道一个其他所有点都能到达的点的出度一定为0(因为缩点后不存在环,所以一旦某个点有出度,则出度所指向的点无法再返回到原来的点了),所以如果有一个出度为0的点,这个点所对应的牛的数量就是答案明星奶牛的数量,但是如果有两个以上的出度为0的点,某两个出度为0的点必定无法相互到达,故不存在题目所说的明星奶牛,为0.

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
const int tvt=;
const int tot=;
using namespace std;
int dfn[tvt],low[tvt],zhan[tvt],out[tvt],head[tot],next[tot],to[tot],belong[tvt],total[tvt],inde,n,m,len,ans,num; //out负责统计点的出度,total负责统计某个强联通分量的点的个数,inde就是时间(戳),ans统计出度为的个数
void add(int u,int v){
num++;
next[num]=head[u];
to[num]=v;
head[u]=num;
}
void tarjan(int u){
dfn[u]=low[u]=++inde;
zhan[++len]=u;
for (int i=head[u];i!=;i=next[i]){
int v=to[i];
if (!dfn[v]){
tarjan(v);
low[u]=min(low[v],low[u]);
}
else low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u])
do{int v=zhan[len--];
belong[v]=u;
out[v]=;
total[u]++;
}while(zhan[len+]!=u); //即(v==u)
}
int main(){
scanf("%d%d",&n,&m);
num=;
memset(next,,sizeof(next));
memset(zhan,,sizeof(zhan));
memset(belong,,sizeof(belong));
memset(total,,sizeof(total));
memset(head,,sizeof(head));
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low));
memset(out,,sizeof(out));
memset(to,,sizeof(to));
for (int i=;i<=m;i++){
int x=,y=;
scanf("%d%d",&x,&y);
add(x,y);//添边
}
len=;
for (int i=;i<=n;i++)
belong[i]=i;
inde=;
for (int i=;i<=n;i++)
if (!dfn[i]) tarjan(i);
for (int i=;i<=n;i++)
for (int j=head[i];j!=;j=next[j]){
int v=to[j];
if (belong[i]!=belong[v]) out[belong[i]]++;//缩点,不在同一个强联通分量的话就视为两个点;把在同一个强联通分量的点的出度都统计到一个点上,达到“缩点效果”
}
ans=;
for (int i=;i<=n;i++){
if ((belong[i]==i)&&(!out[i])) ans++;
if (ans>=) break;
}
if (ans==) for (int i=;i<=n;i++) if ((belong[i]==i)&&(!out[i])){
cout<<total[i]<<endl;
return ;
}
else;
else cout<<''<<endl;
return ;
}

神奇的代码

tarjan后,我们只需要点的出度,那么我们进行“缩点”,考察缩点后点的性质,对于在同一个强联通分量的点它们之间连线产生的出度就不计,就只计不在同一个强联通分量的点的出度,并且对同一个强联通分量的所有点的出度都集中到这个分量的某个点上,这样我们就达到了我们需要的效果的“缩点”。belong数组能够告诉我们两个点是不是在同一个强联通分量里

所以,缩点实际上是根据缩点后的我们需要的性质是怎么变化的,我们就针对那个性质去统计。

强连通分量tarjan缩点——POJ2186 Popular Cows的更多相关文章

  1. POJ 1236 Network of Schools(强连通分量/Tarjan缩点)

    传送门 Description A number of schools are connected to a computer network. Agreements have been develo ...

  2. POJ-2186-Popular Cows(强连通分量,缩点)

    链接:https://vjudge.net/problem/POJ-2186 题意: 有N(N<=10000)头牛,每头牛都想成为most poluler的牛,给出M(M<=50000)个 ...

  3. Tarjan求强连通分量,缩点,割点

    Tarjan算法是由美国著名计算机专家发明的,其主要特点就是可以求强连通分量和缩点·割点. 而强联通分量便是在一个图中如果有一个子图,且这个子图中所有的点都可以相互到达,这个子图便是一个强连通分量,并 ...

  4. Tarjan算法求有向图强连通分量并缩点

    // Tarjan算法求有向图强连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> #inc ...

  5. 洛谷——P2341 [HAOI2006]受欢迎的牛//POJ2186:Popular Cows

    P2341 [HAOI2006]受欢迎的牛/POJ2186:Popular Cows 题目背景 本题测试数据已修复. 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所 ...

  6. Instantaneous Transference(强连通分量及其缩点)

    http://poj.org/problem?id=3592 题意:给出一个n*m的矩阵,左上角代表起始点,每个格子都有一定价值的金矿,其中‘#’代表岩石不可达,‘*’代表时空门可以到达指定格子,求出 ...

  7. ZOJ 3795 Grouping 强连通分量-tarjan

    一开始我还天真的一遍DFS求出最长链以为就可以了 不过发现存在有向环,即强连通分量SCC,有向环里的每个点都是可比的,都要分别给个集合才行,最后应该把这些强连通分量缩成一个点,最后保证图里是 有向无环 ...

  8. 强连通分量(tarjan求强连通分量)

    双DFS方法就是正dfs扫一遍,然后将边反向dfs扫一遍.<挑战程序设计>上有说明. 双dfs代码: #include <iostream> #include <cstd ...

  9. POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】

    Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 23445   Accepted: 9605 Des ...

随机推荐

  1. [1] Ninject

    为什么使用这种依赖注入的框架呢?我借鉴两张图做一下说明 传统的接口方式,即 IValueCalculator I=new LinqValueCalculator,虽然用接口做的隔离,但是在调用的时候实 ...

  2. PHP+MySql实现后台数据的读取

      我们使用的是PHP 的php_mysqli扩展   首先了解一些基础的用法  1.连接数据库使用 mysqli_connect()  参数:①主机地址 ②MYSQL用户名 ③MYSQL密码 ④选择 ...

  3. Mybatis在oracle批量更新

    最近公司业务中为了提高效率要做mybatis批量更新,但是到了oracle数据库中做了好几次都没成功,后来发现mybatis最后少了个分号,可能是Mybatis内部做了异常try  catche  处 ...

  4. JSON总结-持续更新补充

    基本的json格式 { "name": "jobs", "boolean": true, "age": null, &q ...

  5. yii2.0修改默认的访问控制器

  6. Iconfont 矢量图标库的应用

    前言: 在项目开发中,不免在标签栏,工具栏等应用各种各样的小图标. 然后老旧的做法就是要UI设计出各种图标并生成图片给到我们,但是这样就存在了一个问题,每次请求页面的时候就需要发送请求请求图片,这样不 ...

  7. [leetcode-523-Continuous Subarray Sum]

    Given a list of non-negative numbers and a target integer k, write a function to check if the array ...

  8. [leetcode-566-Reshape the Matrix]

    In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new o ...

  9. 自己开源的leaf-snowflake

    拜读了美团点评技术团队博客的"Leaf--美团点评分布式ID生成系统(http://tech.meituan.com/MT_Leaf.html)"之后,收获很多.纸上得来终觉浅 绝 ...

  10. 异常:java.lang.NoSuchMethodError: org.apache.poi.ss.usermodel.Workbook.getCellStyleAt

    背景 最近公司所有新项目要使用最新高效快速开发框架nature-framework,框架本身结合NatureMap已经集成excel的高效导入功能,我们要实现高性能的导出功能,因为最新的jxls-2. ...