【小白入门向】tarjan算法+codevs1332题解报告

一、【前言】关于tarjan

tarjan算法是由Robert Tarjan提出的求解有向图强连通分量的算法。

那么问题来了找蓝翔!(划掉)什么是强连通分量?

我们定义:如果两个顶点互相连通(即存在A到B和B到A的通路),则称这两个点强连通。对于一个有向图G,若是G中任意两点都强连通,则称G是一个强连通图。有向图的极大强连通子图,称为该图的强连通分量。

对于下图,{1,2,3,4}、{5}、{6}分别是它的强连通分量。

那么tarjan是如何找到这些强连通分量的呢?

说白了tarjan就是dfs,每个强连通分量都是搜索树中的一颗子树。搜索时,我们把当前搜索树中未处理过的点加入一个堆栈,回溯时从栈顶依次取出元素判断他们是否属于一个强连通分量。

我们对dfs的过程中添加如下定义:
1、dfn[i]:代表搜索点i的次序编号;

2、low[i]:代表点i所能回溯到的栈中最早的次序编号。

当dfn[i]=low[i]时,以i为根的子树上的所有点便构成一个强连通分量。

二、tarjan算法c语言实现

那么要如何使用程序来实现tarjan算法呢?

大致思路如下:
1、从一个未被处理过的点i开始,给它结合一个次序编号并把它压入栈中,然后标记点表示其已入栈,令low[i]=dfn[i]=次序编号;

2、遍历每条以i为起点的边,若边的终点j未被处理过,就对其进行操作1~3,令low[i]=min(low[i],low[j]);

3、找到的点j若是被处理过,则判断其是否在栈中:若在,则令low[i]=min(low[i],dfn[j]);

4、若是dfn[i]=low[i],就将栈顶到i间的所有元素弹出,它们便是一个强连通分量;

5、重复1~4,直至不存在点未被处理。

贴上伪代码(转自百度百科词条——tarjan算法)

 1 tarjan(u)
 2 {
 3     DFN[u]=Low[u]=++Index//为节点u设定次序编号和Low初值
 4     Stack.push(u)//将节点u压入栈中
 5     for each(u,v) in E//枚举每一条边
 6         if (visnotvisted)//如果节点v未被访问过
 7             tarjan(v)//继续向下找
 8             Low[u]=min(Low[u],Low[v])
 9         else if (vinS)//如果节点v还在栈内
10                 Low[u]=min(Low[u],DFN[v])
11     if (DFN[u]==Low[u])//如果节点u是强连通分量的根
12     repeat{
13         v=S.pop//将v退栈,为该强连通分量中一个顶点
14         printv
15         until(u==v)
16     }

tarjan伪代码

三、codevs1332题解

http://codevs.cn/problem/1332/←_←挂上原题链接

这是一道全♂裸的tarjan题,我们只需跑一遍tarjan,并对所有强连通分量染色,最后输出最大的就好

代码如下:

 1 #include<stdio.h>
 2 #include<algorithm>
 3 using namespace std;
 4 struct node
 5 {
 6     int v;
 7     int next;
 8 }e[50010];//邻接表记录有向图
 9 int stack[5010],top;//栈
10 int dfn[5010],low[5010],index;//index用于记录次序编号
11 bool vis[5010];//判断点是否在栈内
12 int st[5010],cnt;
13 int color[5010],s[5010];//用于染色并记录颜色种类
14 void build(int a,int b)
15 {
16     e[++cnt].v=b;
17     e[cnt].next=st[a];
18     st[a]=cnt;
19 }//建图,没什么好说的
20 void tarjan(int x)
21 {
22     dfn[x]=++index;
23     low[x]=index;
24     vis[x]=true;
25     stack[++top]=x;//当前点入栈
26     int i;
27     for(i=st[x];i!=0;i=e[i].next)//枚举以当前点为起点的边
28     {
29         int temp=e[i].next;//temp为当前被枚举边的终点
30         if(!dfn[temp])//如果当前边终点未被处理
31         {
32             tarjan(temp);
33             low[x]=min(low[x],low[temp]);
34         }
35         else if(vis[temp])low[x]=min(low[x],dfn[temp]);
36     }
37     if(dfn[x]==low[x])
38     {
39         vis[x]=false;
40         color[x]=++num;//给当前强连通分量染上新颜色
41         s[num]++;//给当前强连通分量里的点染色
42         while(stack[top]!=x)//栈顶元素依次出栈
43         {
44             s[num]++;
45             color[stack[top]]=num;
46             vis[stack[top--]]=false;
47         }
48         top--;
49     }
50 }
51 int main()
52 {
53     int n,m,i,a,b,t,ans=0,f;
54     scanf("%d%d",&n,&m);
55     for(i=1;i<=m;i++)
56     {
57         scanf("%d%d%d",&a,&b,&t);
58         build(a,b);
59         if(t-1)build(b,a);
60     }
61     for(i=1;i<=n;i++)
62     {
63         if(!dfn[i])tarjan(i);
64     }
65     for(i=1;i<=n;i++)
66     {
67         if(s[color[i]]>ans)//找到被染色最多的强连通分量(因为要求字典序,所以使用‘>‘)
68         {
69             ans=s[color[i]];
70             f=i;
71         }
72     }
73     printf("%d\n",ans);
74     for(i=1;i<=n;i++)
75     {
76         if(color[i]==color[f])printf("%d ",i);//输出被染成最大强连通分量的颜色的点
77     }
78     return 0;
79 }

Codevs1332

时间: 2024-08-11 16:26:47

【小白入门向】tarjan算法+codevs1332题解报告的相关文章

POJ 2553 The Bottom of a Graph TarJan算法题解

本题分两步: 1 使用Tarjan算法求所有最大子强连通图,并且标志出来 2 然后遍历这些节点看是否有出射的边,没有的顶点所在的子强连通图的所有点,都是解集. Tarjan算法就是模板算法了. 这里使用一个数组和一个标识号,就可以记录这个顶点是属于哪个子强连通图的了. 然后使用DFS递归搜索所有点及其边,如果有边的另一个顶点不属于本子强连通图,那么就说明有出射的边. 有难度的题目: #include <stdio.h> #include <stdlib.h> #include &l

有向图强连通分支的Tarjan算法讲解 + HDU 1269 连通图 Tarjan 结题报告

题目很简单就拿着这道题简单说说 有向图强连通分支的Tarjan算法 有向图强连通分支的Tarjan算法伪代码如下:void Tarjan(u) {dfn[u]=low[u]=++index//进行DFS,每发现一个新的点就对这个点打上时间戳,所以先找到的点时间戳越早,dfn[U]表示最早发现u的时间,low[u]表示u能到达的最早的时间戳.stack.push(u)//将U压入栈中for each (u, v) in E {if (v is not visted)//如果V点没有经历过DFS,则

【强连通分量】tarjan算法及kosaraju算法+例题

阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们要知道两个概念:时间戳(DFN),节点能追溯到的最早的栈中节点的时间戳(LOW).顾名思义,DFN就是在搜索中某一节点被遍历到的次序号(dfs_num),LOW就是某一节点在栈中能追溯到的最早的父亲节点的搜索次序号. Tarjan算法是基于深度优先搜索的算法.在搜索过程中把没有Tarjan过的点入栈

【转】BYV--有向图强连通分量的Tarjan算法

转自beyond the void 的博客: https://www.byvoid.com/zhs/blog/scc-tarjan 注:红色为标注部分 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶

【自己动手写神经网络】小白入门连载(二)--机器人时代必须得有人工神经(不是神经病)

[自己动手写神经网络]小白入门连载(一) 在上一章中,我们已经介绍了神经网络的基本概念.思想,并提及了有关人工神经元模型的部分内容.在本章中,将对人工神经元模型做更多的介绍. 图2.1 多输入生物神经元示意图 在上一章中提到了一个简单的神经元模型,并且该模型只有一个输入p.这意味着只能有一个额外的神经元与之相连接,这显然是不够的.因此,一个实用的神经元必须是可以接受多个输入的,如图2.1所示,神经元拥有3个输入p1.p2和p3.其中,w和b是根据网络情况不断进行调整的,而传入函数s和传输函数f是

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

【自己动手写神经网络】小白入门连载(二):机器人时代必须得有人工神经(不是神经病)

[自己动手写神经网络]小白入门连载(一) 在上一章中,我们已经介绍了神经网络的基本概念.思想,并提及了有关人工神经元模型的部分内容.在本章中,将对人工神经元模型做更多的介绍. 图2.1 多输入生物神经元示意图 在上一章中提到了一个简单的神经元模型,并且该模型只有一个输入p.这意味着只能有一个额外的神经元与之相连接,这显然是不够的.因此,一个实用的神经元必须是可以接受多个输入的,如图2.1所示,神经元拥有3个输入p1.p2和p3.其中,w和b是根据网络情况不断进行调整的,而传入函数s和传输函数f是

2016 年宁波工程学院第七届ACM校赛题解报告

2016 年宁波工程学院第七届ACM校赛题解报告 本题解代码直接为比赛代码,仅供参考. A,B,C,D,G,H,J,K,L,M 来自 Ticsmtc 同学. F 来自 Gealo 同学. E,I 来自Alex 学长. Promblem A :    Two Sum 时间限制: 1 Sec  内存限制: 64 MB 题目描述: 给出n个数,另外给出?个整数S,判断是否可以从中取出2个数,使得这两个数的和是S. 输入: 第?行有个整数T(1 <= T <= 10),代表数据组数. 对于每组数据,第

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先