今天学习了强连通分量。
【参考博客】
如果觉得我讲的有些地方难以理解或者存在问题(欢迎留言),可以看一下我借鉴的一些大佬的博客:
传送门1 传送门2
【知识储备】
首先我们需要对几个定义有一些概念:
强连通:有向图中两个点可以相互到达
强连通图:有向图中任意两个点都是强连通的
强连通分量:一个有向图的一个子图中是强连通图的最大的子图就称这个有向图为强连通分量

通俗理解的话,强连通分量里面的任何两个点都可以相互到达,也就是说至少存在一条路径可以访问到所有的点再回到起点(可以经过重复的点),就好像一个环,因此我们用DFS配合专门的算法来解决求连通分量的问题。

【算法介绍】
我们一般用Trajan算法解决相关问题。
正如上面的简单分析,所谓的强连通分量,就是我们想要找一条可以回到起点的经过尽可能多点的路径,那如何判断我们是回到起点(或者已经访问过的点)呢?我们就需要用数组进行标记每个点的状态来方便我们进行判断。
这里引入两个关键的数组:
DFN[MAXN]DFN[ MAXN ]DFN[MAXN] 用来标记DFS访问到该点时的次序/时间
LOW[MAXN]LOW[MAXN]LOW[MAXN] 用来储存子树(从这一点可以访问到的点)中访问时间最早的,初始化为DFN,也就是自身的访问时间。如果访问到了之前的点,就会变成前面的点的时间戳。

这样对于之前的点来说就形成了一条从自身出发又回到自身的路径,也就是一个强连通分量。

【算法实现】

对于每个点我们进行深搜,对每个点打上时间戳(给DFN和LOW赋值)

然后对每一个没有访问过的点直接进行 访问,并且将LOW的值改为所有后面访问点中最小的。
如果遇到一个访问过的点

如果他不在栈中,就说明他和这个强连通分量没有任何关系(在其他地方已经访问结束,无法到这个点,无法形成回路,否则这个点肯定在栈中)。这里着重需要理解栈中保存的是已经访问过的点中可以访问到这个点的点(其他无关的点都已经弹出)

如果这个已经访问的点在栈中(就比较开心,说明形成环了),如果这个点的最小的时间戳小于他就保存一下LOW,然后这个值就会回溯回去(有可能访问的这个点在另一个小的强连通分量中,所以我觉得应该比较LOW,但是我看其他人的博客都是比较DFN,仔细想了一下觉得也可以,保存DFN的话也可以,但是我还是觉得比较LOW的话LOW的值就可以代表是否存在在同一个强连通分量中,更加优雅一些 。emm,如果觉得不能理解可以先往下看,不要在在意这些细节 )。

最后DFS结束以后再回来看LOW的值是否改变,如果没有改变说明这后面的所有点构成一个强连通分量,然后全部弹出(和他没有关系的早早弹出去了,所以不用担心后面的没有关系的点怎么办)

如果对一个点进行DFS改变LOW的值后LOW的值还没有改变,仍然等于DFN,说明从这一个点出发是无法回到更早的点的,最早也就是自身,也就是说,他一定不是之前点的连通分量里面的(否则通过它肯定能够访问到之前的点,而之前的点的LOW都比较小),所以这个LOW没有改变的点就是一个连通分量的根节点(比较惨的话就只有他一个节点,但也有可能他的子节点会访问到他形成连通分量,但无论如何他都是根节点),我们不妨用一个栈保存访问的顺序,那么他后面访问的点肯定都是他的连通分量中的点,全部弹出即可。(如果不是的话,后面的点肯定自成强连通分量,那么肯定更早弹出了)。 可能稍微有些懵,先有个概念然后再看代码(注意是递归处理的,也就是访问到后面处理完了再返回来处理前面)。

看着代码理解一下吧,觉得哪里不能理解可以再看看上面的分析

void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx; //赋给时间戳stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}

【样例题目】
Popular Cows

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

* Line 1: Two space-separated integers, N and M* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

*Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

题目大意:

有一群牛,他们相互崇拜,找出所有牛都崇拜的牛有多少

输入:牛的个数n 崇拜的关系m,然后m行每行A,B,表示A崇拜B

输出:被所有牛崇拜的牛的个数

【样例分析】
将崇拜的关系变成一个有向图,处于一个强连通分量里面的牛肯定是相互崇拜的,我们将一个强连通分量里面的所有牛看成一个点(染色),不同点重新形成一个图,这个图里面唯一的出度为0的点中牛的个数就是答案。
因为出度为0的点肯定是被其他点里的牛崇拜的,如果出度为0的点大于1的话两个出度为0的点里面的牛是没有办法崇拜的,所以肯定不能被所有的牛崇拜,所以如果出度为0的点不止一个答案就是0,否则就是出度为0 的点里面牛的个数(出度为0的点肯定是大于等于1的,如果没有出度为0 的点那么他们形成了一个环,而我们刚才说这是不同强连通分量,如果只有一个连通分量就只有一个点,也符合一个出度为0的点)

【AC代码】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int MAXN=1e4+5;
const int MAXM=5e4+5;
struct node
{int v,next;
}Edge[MAXM];
int head[MAXN],tot;
int DFN[MAXN],LOW[MAXN];
int color[MAXN],cnt;
bool vis[MAXN];
int idx;
int stack[MAXN],top;
int OutDegree[MAXN];
int n,m;void init()
{memset(head,0,sizeof(head)); tot=0;idx=0; memset(vis,0,sizeof(vis));memset(DFN,0,sizeof(DFN));memset(color,0,sizeof(color));cnt=0; top=0;memset(OutDegree,0,sizeof(OutDegree));
}void read()
{int u,v;for(int i=0;i<m;i++){scanf("%d%d",&u,&v);tot++;Edge[tot].v=v; Edge[tot].next=head[u];head[u]=tot;}
}void Trajan(int x)
{int v,tmp;DFN[x]=LOW[x]=++idx;stack[++top]=x; vis[x]=true;for(int i=head[x];i;i=Edge[i].next){v=Edge[i].v;if(!DFN[v]){Trajan(v);if(LOW[v]<LOW[x]) LOW[x]=LOW[v];}else if(vis[v] && LOW[v]<LOW[x]){LOW[x]=LOW[v];}}if(DFN[x]==LOW[x]){cnt++;do{tmp=stack[top--];vis[tmp]=false;color[tmp]=cnt;}while (tmp!=x);}
}void solve()
{int v,mark,num,ans;for(int i=1;i<=n;i++){if(!DFN[i])Trajan(i);}for(int i=1;i<=n;i++){for(int j=head[i];j;j=Edge[j].next){v=Edge[j].v;if(color[i]!=color[v])OutDegree[color[i]]++;}}num=0; mark=-1;for(int i=1;i<=cnt;i++){if(!OutDegree[i]){num++; mark=i;}}ans=0;if(num!=1){printf("0\n");}else{for(int i=1;i<=n;i++){if(color[i]==mark){ans++;}}printf("%d\n",ans);}
}int main()
{while(~scanf("%d%d",&n,&m)){init();read();solve();}return 0;
}

强连通分量入门——Trajan算法相关推荐

  1. Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法

    一.背景介绍 强连通分量是有向图中的一个子图,在该子图中,所有的节点都可以沿着某条路径访问其他节点.强连通性是一种非常重要的等价抽象,因为它满足 自反性:顶点V和它本身是强连通的 对称性:如果顶点V和 ...

  2. 有向图强连通分量的Tarjan算法——转自BYVoid

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

  3. [转载] 有向图强连通分量的Tarjan算法 ——byvoid

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

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

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

  5. 有向图强连通分量之Tarjan算法

    出处https://www.byvoid.com/zhs/blog/scc-tarjan [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly con ...

  6. 寻找强连通分量的Tarjan算法

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

  7. 算法提高课-图论-有向图的强连通分量-AcWing 367. 学校网络:强连通分量、tarjan算法

    文章目录 题目解答 题目来源 题目解答 来源:acwing 分析: 第一问:通过tarjan算法求出强连通分量并且缩点后,统计入度为0的点的个数p即可. 第二问,至少加几条边才能使图变成强连通分量?这 ...

  8. CSP认证201509-4 高速公路[C++题解]:强连通分量、tarjan算法模板题

    题目分析 来源:acwing 分析: 所求即为强连通分量的个数,然后计算每个强连通分量中点的个数,相加即可. 所谓强连通分量,它是一个子图,其中任意两点可以相互到达,并且再加一个点,就不能满足任意两点 ...

  9. 程序设计思维 C - 班长竞选 (强连通分量、kosaraju算法)

    题目 大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了 ...

  10. 图论——强连通分量(Tarjan算法)

    文章目录 强连通分量 利用Tarjan算法求强连通分量 来一道例题练手(USACO08DEC) 图论文章汇总 强连通分量 什么是强连通图? 如果一个有向图中,存在一条回路,所有的结点至少被经过一次,这 ...

最新文章

  1. python的难点在哪里_自己写的Python答案,不知道错在哪儿希望能被告知问题在哪儿和答案...
  2. 细说SSO单点登录(转)
  3. while和for循环读取大文件三种读取文件方式
  4. mysql架设_服务器架设MySQL开发规范与使用技巧
  5. Vxworks系统学习之一----任务
  6. IDEA通过Database连接MySql数据库
  7. 数据库代码编写_如何将您的职业转变为数据科学-即使您今天不编写代码。
  8. 关于iOS 'The sandbox is not sync with the Podfile.lock'问题解决方法
  9. 找2个数组中相同的数
  10. 2021年营销数智化趋势洞察报告:深链经营孕育品牌发展新商机.pdf(附下载链接)...
  11. HTTP Error 404 - File or Directory not found caused by ISAPI filter of Sharepoint
  12. 第十节 直流变直流电路(DCDC)芯片选型
  13. Python 图片尺寸缩放的4种方式
  14. 小故事大道理——生存方略
  15. 计算机写给未来自己的一段话,现实,致自己 写给自己的霸气一段话汇总73句
  16. 用Go实现UTXO,UTXO实现
  17. 猜数字小游戏(JAVA)
  18. 安装麦咖啡8.7出现--错误1920
  19. [硬件项目] 1、汽车倒车雷达设计——基于API8108A芯片简易智能语音模块的设计与实现...
  20. 【修真院java小课堂】ArrayList浅析

热门文章

  1. 杯具啊,混合语言编程的弊端出现了,兼谈js的开发工具
  2. 久期方程 matlab,有限差分法解薛定谔方程与MATLAB实现
  3. Adobe illustrator 常用菜单命令和快捷键
  4. 程序员为什么要会用Google谷歌搜索引擎
  5. 服务器搬迁该怎么做?要准备什么
  6. 项目管理 - 常用英语词汇一
  7. ARM 发布新一代 CPU 和 GPU,实现 20% 性能提升!
  8. 七款好看文字样式纯css
  9. linux服务器运维工程师怎么样,怎样才算合格的运维工程师?linux运维技术
  10. easyexcel 导出数据锁定某个单元格